golangtutorialAll tutorials

Go Context: Cancellation, Deadlines, and Request Values

Learn Go context with practical cancellation, timeout, HTTP request, and goroutine examples.

Concurrency · Lesson 1Saved in this browser. No account required.

context.Context carries cancellation signals, deadlines, and request-scoped values across API boundaries. Its most important job is letting work stop when the caller no longer needs it.

Start with cancellation

Create a derived context and always call its cleanup function:

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

go func() {
	select {
	case <-ctx.Done():
		fmt.Println("stopped:", ctx.Err())
	case <-time.After(time.Second):
		fmt.Println("finished")
	}
}()

cancel()

Closing ctx.Done() broadcasts cancellation to every listener. ctx.Err() explains whether cancellation or a deadline caused the stop.

Add a deadline or timeout

ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()

if err := fetch(ctx); err != nil {
	log.Println(err)
}

Pass the context down; do not replace it with context.Background() inside fetch. Each layer may shorten a deadline, but should not silently discard its caller’s cancellation.

Make blocking work context-aware

func fetch(ctx context.Context) error {
	select {
	case <-time.After(time.Second):
		return nil
	case <-ctx.Done():
		return ctx.Err()
	}
}

For HTTP clients, use http.NewRequestWithContext. Database APIs such as QueryContext follow the same pattern.

Use values sparingly

Context values are for request-scoped metadata such as a trace or request ID, not configuration or optional function parameters. Use a private key type to prevent collisions.

type requestIDKey struct{}
ctx = context.WithValue(ctx, requestIDKey{}, "req-123")

Accept a context as the first parameter, never store it in a struct unless an API specifically requires it, and never pass nil. These rules keep cancellation ownership visible.

Common mistakes

  • Forgetting cancel, which retains timers and resources longer than necessary.
  • Starting goroutines that never select on ctx.Done().
  • using context values as a general-purpose dependency container.
  • Logging every context.Canceled as a server failure when the client simply disconnected.

Context is most useful when every layer cooperates. A cancellation signal cannot stop code that never checks it.