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.