Documentation Index
Fetch the complete documentation index at: https://mintlify.com/golang/go/llms.txt
Use this file to discover all available pages before exploring further.
The context package defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
The Context Interface
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
Creating Contexts
Background and TODO
import "context"
// Background returns a non-nil, empty Context
// Used as the top-level Context for incoming requests
ctx := context.Background()
// TODO returns a non-nil, empty Context
// Use when it's unclear which Context to use or not yet available
ctx := context.TODO()
WithCancel
Create a context that can be manually cancelled.
import (
"context"
"fmt"
"time"
)
func withCancelExample() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // Always call cancel to release resources
go func() {
time.Sleep(2 * time.Second)
cancel() // Cancel the context
}()
select {
case <-time.After(5 * time.Second):
fmt.Println("Operation completed")
case <-ctx.Done():
fmt.Println("Operation cancelled:", ctx.Err())
}
}
WithTimeout and WithDeadline
Create contexts that cancel automatically after a duration or at a specific time.
// WithTimeout: cancel after duration
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// WithDeadline: cancel at specific time
deadline := time.Now().Add(10 * time.Second)
ctx, cancel = context.WithDeadline(context.Background(), deadline)
defer cancel()
WithValue
Carry request-scoped data through the call chain.
type key string
const userKey key = "user"
func withValueExample() {
ctx := context.WithValue(context.Background(), userKey, "alice")
// Retrieve value
if user, ok := ctx.Value(userKey).(string); ok {
fmt.Printf("User: %s\n", user)
}
}
Practical Examples
HTTP Request with Timeout
import (
"context"
"io"
"net/http"
"time"
)
func makeRequest(url string) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
_, err = io.ReadAll(resp.Body)
return err
}
Database Query with Context
import (
"context"
"database/sql"
"time"
)
func queryDatabase(db *sql.DB, userID int) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var name string
err := db.QueryRowContext(ctx, "SELECT name FROM users WHERE id = ?", userID).Scan(&name)
if err != nil {
return err
}
fmt.Printf("User: %s\n", name)
return nil
}
Worker Pool with Cancellation
func worker(ctx context.Context, id int, jobs <-chan int, results chan<- int) {
for {
select {
case <-ctx.Done():
fmt.Printf("Worker %d cancelled\n", id)
return
case job, ok := <-jobs:
if !ok {
return
}
results <- job * 2
}
}
}
func workerPool() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
jobs := make(chan int, 100)
results := make(chan int, 100)
// Start workers
for i := 1; i <= 3; i++ {
go worker(ctx, i, jobs, results)
}
// Send jobs
for i := 1; i <= 5; i++ {
jobs <- i
}
close(jobs)
// Get results or cancel
for i := 1; i <= 5; i++ {
select {
case result := <-results:
fmt.Printf("Result: %d\n", result)
case <-time.After(1 * time.Second):
cancel() // Cancel all workers
return
}
}
}
Chained Context Operations
func processRequest(ctx context.Context) error {
// Add timeout to incoming context
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
// Add request ID to context
ctx = context.WithValue(ctx, "requestID", "req-123")
return performOperation(ctx)
}
func performOperation(ctx context.Context) error {
select {
case <-time.After(2 * time.Second):
requestID := ctx.Value("requestID")
fmt.Printf("Operation completed for %v\n", requestID)
return nil
case <-ctx.Done():
return ctx.Err()
}
}
WithCancelCause (Go 1.20+)
Provide a reason for cancellation.
import (
"context"
"errors"
"fmt"
)
func withCancelCauseExample() {
ctx, cancel := context.WithCancelCause(context.Background())
go func() {
time.Sleep(1 * time.Second)
cancel(errors.New("operation timed out"))
}()
<-ctx.Done()
fmt.Println("Context cancelled:", context.Cause(ctx))
}
Best Practices
1. Pass Context as First Parameter
// Good
func DoSomething(ctx context.Context, arg string) error {
// ...
}
// Bad
func DoSomething(arg string, ctx context.Context) error {
// ...
}
2. Don’t Store Context in Structs
// Bad
type Server struct {
ctx context.Context
}
// Good - pass context to methods
type Server struct {}
func (s *Server) HandleRequest(ctx context.Context) error {
// ...
}
3. Always Call Cancel Functions
func goodExample() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() // Always defer cancel
// Use ctx...
}
4. Don’t Pass nil Context
// Bad
DoSomething(nil, "arg")
// Good - use context.TODO if unsure
DoSomething(context.TODO(), "arg")
5. Use Typed Keys for Context Values
// Good - unexported type prevents collisions
type contextKey string
const userIDKey contextKey = "userID"
func SetUserID(ctx context.Context, id string) context.Context {
return context.WithValue(ctx, userIDKey, id)
}
func GetUserID(ctx context.Context) (string, bool) {
id, ok := ctx.Value(userIDKey).(string)
return id, ok
}
Context Errors
var (
Canceled = errors.New("context canceled")
DeadlineExceeded = errors.New("context deadline exceeded")
)
// Check error type
if err := ctx.Err(); err != nil {
if errors.Is(err, context.Canceled) {
// Context was cancelled
} else if errors.Is(err, context.DeadlineExceeded) {
// Timeout occurred
}
}
When to Use Context
Use Context for:
- Request timeouts and deadlines
- Cancellation signals across goroutines
- Request-scoped values (user ID, trace ID, etc.)
- Propagating cancellation through call chains
Don’t Use Context for:
- Optional function parameters
- Storing application state
- Passing values that should be function arguments
- Long-lived background operations that shouldn’t be cancelled
Common Patterns
Graceful Shutdown
func main() {
ctx, cancel := context.WithCancel(context.Background())
// Handle shutdown signals
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
go func() {
<-sigChan
cancel() // Cancel context on signal
}()
runServer(ctx)
}
func runServer(ctx context.Context) {
// Server operations that respect ctx.Done()
}
Timeout with Retry
func retryWithTimeout(ctx context.Context, attempts int, delay time.Duration, fn func() error) error {
for i := 0; i < attempts; i++ {
if err := fn(); err == nil {
return nil
}
select {
case <-time.After(delay):
// Continue to next attempt
case <-ctx.Done():
return ctx.Err()
}
}
return fmt.Errorf("failed after %d attempts", attempts)
}