Go context Package: Cancel, Timeout, Deadlines, and Values

Use golang context for deadlines and cancellation: Background and TODO roots, WithCancel, WithTimeout, WithDeadline, and WithValue for request-scoped data; always call cancel and pass ctx as the first parameter.

Published

Updated

Read time 3 min read

Reviewed byDeepak Prasad

Go context Package: Cancel, Timeout, Deadlines, and Values

The context package defines context.Context, an interface for deadlines, cancellation, and request-scoped values across API boundaries (package doc). Typical entry points are context.Background(), context.WithCancel, context.WithTimeout / WithDeadline, and context.WithValue. For related concurrency patterns, see goroutines and channels.

Tested with Go 1.24 on Linux.


The Context interface

go
type Context interface {
	Deadline() (deadline time.Time, ok bool)
	Done() <-chan struct{}
	Err() error
	Value(key any) any
}
  • Deadline — when the context will cancel, if any.
  • Done — closed when work should stop.
  • Err — non-nil after cancellation (context.Canceled or context.DeadlineExceeded).
  • Value — optional request data; use sparingly.

Roots: Background and TODO

context.Background() never cancels, has no deadline, and carries no values—use it as the root in main, tests, or the top of an inbound request before attaching timeouts.

context.TODO() is a placeholder when you cannot yet thread a real context; replace it once a proper parent exists.

Run the snippet: printing Background shows the empty-root context string; Err() stays nil until canceled.

go
package main

import (
	"context"
	"fmt"
)

func main() {
	ctx := context.Background()
	fmt.Println(ctx)
	fmt.Println("Err:", ctx.Err())
}
Output

Time limits: WithTimeout and WithDeadline

WithTimeout(parent, d) and WithDeadline(parent, t) return a child context and a cancel function. Always call cancel() (usually defer cancel()) to free the timer even if the body returns early.

go
package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
	defer cancel()

	select {
	case <-time.After(time.Second):
		fmt.Println("overslept")
	case <-ctx.Done():
		fmt.Println(ctx.Err())
	}
}
Output

You should see context deadline exceeded because the timeout fires before one second elapses.


Manual cancel: WithCancel

Use WithCancel when another goroutine or event should stop work (not only a clock).

go
package main

import (
	"context"
	"fmt"
)

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

	ch := make(chan int)
	go func() {
		for n := 1; ; n++ {
			select {
			case <-ctx.Done():
				return
			case ch <- n:
			}
		}
	}()

	for n := range ch {
		fmt.Println(n)
		if n == 3 {
			cancel()
			break
		}
	}
}
Output

You should see 1, 2, 3 printed.


Request values: WithValue

WithValue(parent, key, val) returns a child that exposes Value(key). Keys should be unexported types (for example type ctxKey int) to avoid collisions between packages—never use bare strings for library keys.

go
package main

import (
	"context"
	"fmt"
)

type ctxKey int

const sessionKey ctxKey = 1

func main() {
	ctx := context.WithValue(context.Background(), sessionKey, "abc-123")
	v := ctx.Value(sessionKey)
	fmt.Println(v.(string))
}
Output

You should see abc-123.


Practices

  • Put ctx context.Context first in function parameters.
  • Prefer the variable name ctx.
  • defer cancel() whenever you obtain cancel from WithCancel, WithTimeout, or WithDeadline.
  • Avoid stuffing optional parameters into WithValue.

Summary

The golang context package ties cancellation, timeouts, and optional request values to a Context value you pass through your call stack. Use Background (or TODO temporarily) as the root, WithTimeout / WithDeadline for time bounds, WithCancel for explicit shutdown, and WithValue only for small, request-scoped metadata—always defer cancel() when the API returns a cancel function.


References


Frequently Asked Questions

1. What is context.Context in Go?

It is an interface carrying a deadline, cancellation channel, and optional key-value data across API boundaries. Prefer passing context.Context as the first parameter to functions that do I/O or long work.

2. When should I use context.Background versus TODO?

Use Background as the root in main, tests, or top-level handlers. Use TODO only as a temporary placeholder when you have not yet threaded a real context from a caller.

3. Why must I call the cancel function from WithCancel or WithTimeout?

Cancel releases timers and goroutine resources tied to the child context. Defer cancel() immediately after creating the context unless the API lifetime is different and you document it.

4. Is context.WithValue a good place for optional function parameters?

No. WithValue is for request-scoped metadata such as trace IDs. Use normal parameters for optional behavior; overusing WithValue obscures APIs.

5. What does ctx.Done() indicate?

A closed channel when the context is canceled or its deadline passes. Select on ctx.Done() alongside work or time.After to stop early.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …