Golang Context
Samuel BertheCommunityCreated by samberDistributed by Samuel Berthe
- Skill
- Guidelines
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a comprehensive guide to using context.Context in Go applications. It covers the essential patterns for propagating cancellation signals, deadlines, and request-scoped values across API boundaries. It emphasizes the importance of maintaining a consistent context chain throughout the request lifecycle, proper resource management with cancel functions, and the correct use of context-aware APIs for HTTP and database operations.
What it does
- Defines patterns for propagating context through the entire request lifecycle.
- Explains how to manage cancellation, timeouts, and deadlines using context.
- Provides guidelines for storing and retrieving request-scoped values safely.
- Outlines the correct usage of context.Background, context.TODO, and context.WithoutCancel.
- Demonstrates how to integrate context into HTTP servers and downstream service calls.
- Highlights common pitfalls like context leaks and improper context creation.
When to use it
- When designing or refactoring context propagation across application layers.
- When debugging issues related to leaked contexts or unexpired deadlines.
- When implementing background tasks that need to outlive the parent request.
- When creating middleware that requires request-scoped metadata.
- When ensuring database or HTTP client operations respect request cancellation.
Advantages
- Ensures consistent cancellation behavior across complex call chains.
- Prevents resource leaks by enforcing proper deferral of cancel functions.
- Improves code maintainability through idiomatic context handling.
- Reduces bugs related to context-aware API misuse.
- Facilitates better observability through standardized trace context propagation.
Usage examples
- “How should I pass a context to a database query in a service method?”
- The agent will demonstrate using the context passed as the first parameter to the service method and passing it directly into the database's QueryContext or ExecContext method.
- “I need to spawn a background task that shouldn't be cancelled when the HTTP request finishes. How do I do that?”
- The agent will explain how to use context.WithoutCancel to detach the background task from the request's context while preserving other necessary values.
- “What is the best way to store a user ID in a context?”
- The agent will provide an example of defining an unexported custom type for the context key to prevent collisions and using context.WithValue to store the data.
- “Why is my application leaking resources when using context.WithTimeout?”
- The agent will identify the missing defer cancel() call and explain why it is necessary to release timer resources.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/samber/golang-context/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip golang-context.zip -d .agents/skills
Documentation by samber
Community default. A company skill that explicitly supersedes
samber/cc-skills-golang@golang-contextskill takes precedence.
Go context.Context Best Practices
context.Context is Go's mechanism for propagating cancellation signals, deadlines, and request-scoped values across API boundaries and between goroutines. Think of it as the "session" of a request — it ties together every operation that belongs to the same unit of work.
Best Practices Summary
Propagate the same context through the entire request lifecycle: HTTP handler → service → DB → external APIs — any link that starts a fresh context keeps working after the client is gone.
Take
ctxas the first parameter, namedctx context.Context— the fixed position is what makes context-aware APIs recognizable at a glance and what linters check.Pass context through function parameters instead of storing it in a struct — the struct outlives the request that filled it, so later calls reuse a context that is already cancelled or belongs to someone else.
Pass
context.TODO()rather than anilcontext —nilpanics on the firstDone()orValue()call, far from the caller that passed it.Call
cancel()on all control-flow paths forWithCancel/WithTimeout/WithDeadline, unless ownership of the context and cancel function is explicitly returned or transferred — an uncalledcancel()keeps the child attached to its parent and leaks its timer until the parent finishes.Create
context.Background()only at top-level entry points (main, init, tests). Deeper in the call chain — especially mid-request — it detaches the work from the caller's deadline and cancellation, the propagation break shown below.Use
context.TODO()as a placeholder when a context is needed but none exists yet — it marks the gap for a later fix instead of hiding it behind aBackground()that looks deliberate.Declare context value keys as unexported types — with a plain
stringkey, two packages using"user"silently overwrite each other.Carry only request-scoped metadata in context values, never function parameters — values retrieved through
Value()lose compile-time typing and disappear from the function signature.Use
context.WithoutCancel(Go 1.21+) when spawning background work that must outlive the parent request — otherwise the handler returning cancels the audit log or cleanup just started.
Creating Contexts
| Situation | Use |
|---|---|
| Entry point (main, init, test) | context.Background() |
| Function needs context but caller doesn't provide one yet | context.TODO() |
| Inside an HTTP handler | r.Context() |
| Need cancellation control | context.WithCancel(parentCtx) |
| Need a deadline/timeout | context.WithTimeout(parentCtx, duration) |
Context Propagation: The Core Principle
The most important rule: propagate the same context through the entire call chain. When you propagate correctly, cancelling the parent context cancels all downstream work automatically.
// ✗ Bad — creates a new context, breaking the chain
func (s *OrderService) Create(ctx context.Context, order Order) error {
return s.db.ExecContext(context.Background(), "INSERT INTO orders ...", order.ID)
}
// ✓ Good — propagates the caller's context
func (s *OrderService) Create(ctx context.Context, order Order) error {
return s.db.ExecContext(ctx, "INSERT INTO orders ...", order.ID)
}Deep Dives
Cancellation, Timeouts & Deadlines — How cancellation propagates:
WithCancelfor manual cancellation,WithTimeoutfor automatic cancellation after a duration,WithDeadlinefor absolute time deadlines. Patterns for listening (<-ctx.Done()) in concurrent code,AfterFunccallbacks, andWithoutCancelfor operations that must outlive their parent request (e.g., audit logs).Context Values & Cross-Service Tracing — Safe context value patterns: unexported key types to prevent namespace collisions, when to use context values (request ID, user ID) vs function parameters. Trace context propagation: OpenTelemetry trace headers, correlation IDs for log aggregation, and marshaling/unmarshaling context across service boundaries.
Context in HTTP Servers & Service Calls — HTTP handler context:
r.Context()for request-scoped cancellation, middleware integration, and propagating to services. HTTP client patterns:NewRequestWithContext, client timeouts, and retries with context awareness. Database operations: always use*Contextvariants (QueryContext,ExecContext) to respect deadlines.
Cross-References
→ See the
samber/cc-skills-golang@golang-concurrencyskill for goroutine cancellation patterns using context→ See the
samber/cc-skills-golang@golang-databaseskill for context-aware database operations (QueryContext, ExecContext)→ See the
samber/cc-skills-golang@golang-observabilityskill for trace context propagation with OpenTelemetry→ See the
samber/cc-skills-golang@golang-design-patternsskill for timeout and resilience patterns
Enforce with Linters
Many context pitfalls are caught automatically by linters: govet, staticcheck. → See the samber/cc-skills-golang@golang-lint skill for configuration and usage.
Files
- ATTRIBUTION.mdAdded by AutoGineer899 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md6.2 KiB
- evals/evals.json13.2 KiB
- references/cancellation.md5.9 KiB
- references/http-services.md3.7 KiB
- references/values-tracing.md3.1 KiB
references/values-tracing.md
Context Values & Cross-Service Tracing
Using context values correctly
Context values carry request-scoped metadata that crosses API boundaries — not function parameters, configuration, or optional arguments. Good candidates: trace IDs, span IDs, request IDs, authenticated user info, correlation IDs.
Always use an unexported type as the key to prevent collisions between packages:
// ✓ Good — unexported key type prevents collisions
type contextKey string
const (
traceIDKey contextKey = "trace_id"
requestIDKey contextKey = "request_id"
)
func WithTraceID(ctx context.Context, traceID string) context.Context {
return context.WithValue(ctx, traceIDKey, traceID)
}
func TraceIDFromContext(ctx context.Context) (string, bool) {
traceID, ok := ctx.Value(traceIDKey).(string)
return traceID, ok
}// ✗ Bad — string keys collide across packages
ctx = context.WithValue(ctx, "trace_id", traceID) // another package could use the same keyWhat belongs in context values vs function parameters
| Data | Context value? | Why |
|---|---|---|
| trace_id, span_id, request_id | Yes | Request-scoped metadata for observability |
| Authenticated user/tenant | Yes | Request-scoped, crosses API boundaries |
| Database connection | No | Infrastructure dependency, pass explicitly |
| Feature flags | No | Configuration, pass explicitly or inject |
| Function arguments (user ID, order data) | No | Business logic parameters, pass as arguments |
| Logger | Depends | OK if enriched with request-scoped fields (trace_id); otherwise pass explicitly |
Trace propagation between services
In a microservices architecture, context.Context is the vehicle for trace propagation. When Service A calls Service B, the trace_id and span_id travel through context values and are injected into outgoing HTTP headers (typically via OpenTelemetry). This creates a connected trace across the entire request path.
// Middleware injects trace_id from incoming request headers into context
func TracingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
traceID := r.Header.Get("X-Trace-ID")
if traceID == "" {
traceID = generateTraceID()
}
ctx := WithTraceID(r.Context(), traceID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// When making outbound HTTP calls, inject trace_id from context into headers
func (c *HTTPClient) Do(ctx context.Context, method, url string, body io.Reader) (*http.Response, error) {
req, err := http.NewRequestWithContext(ctx, method, url, body)
if err != nil {
return nil, fmt.Errorf("creating request: %w", err)
}
// Propagate trace_id to downstream service
if traceID, ok := TraceIDFromContext(ctx); ok {
req.Header.Set("X-Trace-ID", traceID)
}
return c.client.Do(req)
}With OpenTelemetry, this propagation is handled automatically through the otel SDK and propagation.TraceContext, but the mechanism is the same: context carries the trace state, and it must be propagated through every layer.
Requirements and compatibility
Written for: Claude Code, OpenAI Codex
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Declares allowed tools, Claude Code frontmatter
The author says: Designed for Claude Code, Codex or similar harness, and for projects using Golang.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- samber
- Publisher
- Samuel Berthe (Community)
- Distributed by
- Samuel Berthe
- Source
- samber/cc-skills-golang · skills/golang-context · commit bb1d43d
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by samber or Samuel Berthe.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download golang-context.zip