Go

What is WithCancelCause, and how does ctx.Err() differ from context.Cause(ctx)?

Question 265HardGo 1.22 to 1.25

WithCancelCause (Go 1.20) returns a CancelCauseFunc that takes an error saying why the context was canceled. ctx.Err() still returns only context.Canceled, which keeps the Context interface stable. context.Cause(ctx) returns the specific reason.

var ErrQuotaExceeded = errors.New("tenant quota exceeded")

ctx, cancel := context.WithCancelCause(context.Background())
cancel(ErrQuotaExceeded)
cancel(errors.New("ignored")) // first cause wins

fmt.Println(ctx.Err())           // context canceled
fmt.Println(context.Cause(ctx))  // tenant quota exceeded

How Cause behaves:

  • Not yet canceled: it returns nil.
  • Canceled with cancel(nil): the cause is context.Canceled.
  • Canceled by a parent: the child gets the parent's cause.
  • Context never created with a cause-aware constructor: Cause returns ctx.Err().

Use case: in fan-out, the first failing worker calls cancel(err). Every sibling then sees ctx.Err() == Canceled, while the coordinator reports context.Cause(ctx) as the real root cause. This is how golang.org/x/sync/errgroup works internally when built with Go 1.20 or later: the first error becomes the derived context's cause.

More on Context

All 35 Context questions