The context Package

A context.Context carries cancellation signals, deadlines, and request-scoped values across API and goroutine boundaries, so a single timeout or cancel at the top of a request can cleanly unwind every goroutine working on it.

Learn The context Package in our free Go course — an interactive lesson with runnable examples, a practice exercise and a quick reference.

Part of the free Go course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

1️⃣ WithCancel , Done() and Err()

Every context tree starts at context.Background() — empty and never cancelled. context.WithCancel(parent) returns a child and a cancel function. A goroutine watches ctx.Done() — a channel that gets closed when you cancel — and ctx.Err() tells it why ( context.Canceled or context.DeadlineExceeded ).

2️⃣ WithTimeout and WithDeadline

WithTimeout(parent, d) gives you a context that cancels itself after duration d ( WithDeadline takes an absolute time instead). Your task races its real work against ctx.Done() in a select ; whichever fires first wins. Always defer cancel() — even on success — to release the underlying timer.

3️⃣ WithValue and cancellation propagation

WithValue attaches request-scoped data — an ID, a trace, an auth identity — that travels with the request. Use an unexported key type to avoid collisions, and reserve it for data that crosses API boundaries (not ordinary parameters). The second half shows propagation : cancelling a parent cancels every child derived from it.

🎯 Your Turn

Build a context with a deadline and react to it. Fill in the two blanks marked ___ , then run it.

❌ Forgetting cancel() after WithTimeout — leaks the timer until the parent dies. go vet warns.

❌ Passing nil as a context — panics on the first ctx.Done() / Value .

✅ Use context.Background() or context.TODO() instead.

❌ Storing a context in a struct field and reusing it across requests.

✅ Pass ctx explicitly as the first argument of each call.

❌ Using WithValue for ordinary parameters — values are untyped and easy to misread.

✅ Pass real arguments; reserve context values for request-scoped data with a custom key type.

context deadline exceeded — a zero (or already-passed) deadline cancels the context immediately, so Done() is already closed.

No. Cancellation flows parent → child, never the other way. Cancelling a child leaves the parent (and its siblings) running.

Implement search so it races its backend work against ctx.Done() with a select . With a 40ms deadline and 200ms work, it should return context deadline exceeded .

Practice quiz

What is the root context that every context tree starts from?

  • context.Root()
  • context.New()
  • context.Background()
  • context.Empty()

Answer: context.Background(). context.Background() is the empty, never-cancelled root from which child contexts are derived.

What does ctx.Done() return?

  • A channel that is closed when the context is cancelled
  • An error
  • A bool
  • The deadline time

Answer: A channel that is closed when the context is cancelled. Done() returns a channel that is closed on cancellation, so every goroutine selecting on it wakes at once.

What does ctx.Err() report after a WithTimeout deadline passes?

  • context.Canceled
  • nil
  • io.EOF
  • context.DeadlineExceeded

Answer: context.DeadlineExceeded. When the timeout trips, Err() returns context.DeadlineExceeded ('context deadline exceeded').

Why must you call cancel() even when using WithTimeout?

  • To print the error
  • To release the timer and resources tied to the context
  • To restart the timer
  • It is optional and has no effect

Answer: To release the timer and resources tied to the context. cancel() frees the internal timer and child-tracking goroutine; skipping it leaks them until the parent is cancelled.

If you cancel a PARENT context, what happens to its children?

  • All children are cancelled too
  • Nothing
  • Only direct children
  • They restart

Answer: All children are cancelled too. Cancellation propagates down the tree: cancelling a parent cancels every descendant context.

If you cancel a CHILD context, does the parent get cancelled?

  • Yes
  • Only with WithTimeout
  • No — cancellation only flows parent to child
  • Only the root

Answer: No — cancellation only flows parent to child. Cancellation flows parent to child, never the other way; cancelling a child leaves the parent running.

By convention, where does the context argument go in a function signature?

  • Last
  • First, named ctx
  • In a struct field
  • It is a global

Answer: First, named ctx. The convention is ctx context.Context as the first parameter; never store it in a struct or pass nil.

What should you pass instead of a nil context?

  • An empty struct
  • context.Nil()
  • A closed channel
  • context.Background() or context.TODO()

Answer: context.Background() or context.TODO(). Passing nil panics on ctx.Done()/Value(); use context.Background() or context.TODO() instead.

What kind of data belongs in context.WithValue?

  • Optional function parameters
  • Request-scoped data crossing API boundaries (IDs, auth)
  • Large config objects
  • Database connections

Answer: Request-scoped data crossing API boundaries (IDs, auth). WithValue is for request-scoped data like a request ID or auth identity — not ordinary parameters or dependencies.

How does a long task respond to cancellation?

  • It cannot
  • Sleep until the deadline
  • Select on ctx.Done() or check ctx.Err() and return early
  • Call cancel() itself

Answer: Select on ctx.Done() or check ctx.Err() and return early. Tasks select on ctx.Done() alongside real work, or check ctx.Err() each iteration, returning early when cancelled.