Explain context errors: context.Canceled, DeadlineExceeded, and context.Cause.
ctx.Err() returns nil while the context is active. After it ends, it returns context.Canceled (from an explicit cancel) or context.DeadlineExceeded (from a timeout). DeadlineExceeded implements Timeout() bool returning true, so it looks like a net.Error timeout. Operations such as http.Client.Do and db.QueryContext wrap these errors, so check them with errors.Is.
Go 1.20 added WithCancelCause and context.Cause, and Go 1.21 added WithTimeoutCause and WithDeadlineCause, so you can record why a context was cancelled while Err() keeps returning the standard values:
var ErrShutdown = errors.New("server shutting down")
ctx, cancel := context.WithCancelCause(context.Background())
cancel(ErrShutdown)
fmt.Println(ctx.Err()) // context canceled
fmt.Println(context.Cause(ctx)) // server shutting down
fmt.Println(errors.Is(ctx.Err(), context.Canceled)) // true
tctx, stop := context.WithTimeoutCause(context.Background(),
50*time.Millisecond, errors.New("payment API too slow"))
defer stop()
<-tctx.Done()
fmt.Println(tctx.Err(), "|", context.Cause(tctx))
// context deadline exceeded | payment API too slow
In practice, don't log a client-cancelled request (context.Canceled) as a server error, and map DeadlineExceeded to 504 or retry logic. context.AfterFunc (Go 1.21) runs cleanup when a context ends.
More on Error Handling & panics
- Q318What does this print? (Shadowed
err) - Q319What does
fmt.Errorf("op: %w", err)return whenerris nil? Why does this matter in helper functions? - Q321Why is
errors.Is(err, fs.ErrNotExist)preferred overos.IsNotExist(err)? - Q322What is "asserting errors for behavior"? How would you implement retry logic based on error behavior?
- Q323How does
net/httphandle a panic in a handler? What are the pitfalls? - Q324How would you design an error model for a large service, for example with Op, Kind and a wrapped cause?