Review Go code for context bugs, goroutine leaks, and memory issues. Use when user mentions Go code problems with context, goroutines, background workers, or asks to audit/debug code involving ctx. Catches leaks, missing cancel(), stored ctx, time.After in loops, and WithValue anti-patterns.
Review Go code for context.Context misuse and report findings with risk-ranked, actionable repair guidance.
Context bugs are insidious because they compile, pass basic tests, and only surface under production load as goroutine leaks, stale data, or cascading timeouts. This skill helps catch them early.
Use this skill when the user asks to:
ctx propagation, timeout handling, or deadline managementDo not use for non-Go concurrency reviews or purely stylistic Go feedback.
Start with the smallest relevant scope, then expand only when evidence warrants it:
context.Context stored in struct fields, caches, or long-lived objectsctx.Done()context.WithCancel / WithTimeout / WithDeadline created without calling cancelctx and replaces it with context.Background() or context.TODO() inside library or handler codetime.After() inside a select loop — each iteration allocates a timer that cannot be garbage collected until it fires, causing memory leaks under loadcontext.WithValue used for required business inputs (user IDs, tenant IDs, order IDs, auth decisions, pagination)ctx is not the first parameter of a functionctxerrgroup.WithContext used but the original (non-derived) context is passed to child goroutines, bypassing the group's cancellationselect with a default case that creates a hot-spin loop when channels are emptyvalue, ok := <-ch for safe exitcontext.WithoutCancel (Go 1.21+) used to detach work — intentional but deserves scrutiny for lifetime ownershipThe code can leak goroutines, timers, or request-scoped resources, or stores request context in long-lived state.
Examples:
ctx.Done()WithTimeout is created in a hot path and cancel is never calledctxtime.After is used inside a for/select loop (timer leak on every iteration)The code obscures cancellation, smuggles business data through context, or silently drops the upstream context, but the immediate leak risk is indirect.
Examples:
context.WithValue carries userID, tenantID, or orderIDctx with context.Background()errgroup.WithContext is used but children receive the wrong ctxcontext.WithoutCancel detaches work without documented ownershipConvention issues that reduce clarity but are unlikely to leak by themselves.
Examples:
ctx is not the first parametercontext.TODO() appears in code that should have a real context by nowAvoid over-reporting:
context.WithValue is acceptable for trace IDs, request IDs, log correlation, and span contextWhen uncertain, state the uncertainty and what evidence would confirm or refute the finding.
For detailed detection heuristics and edge cases, read references/patterns.md.
ctx, trace where it was created and where it should be canceled.cancel called on all exit paths.WithValue: distinguish cross-cutting metadata from business-required inputs.Return Markdown. For each finding:
## Finding N
**Risk:** High | Medium | Low
**Location:** `path/to/file.go:12-24`
**Issue:**
Explain what the code does, why it is risky, and what failure mode it can trigger under load or cancellation.
**Fix:**
```go
// minimal targeted fix
```
If there are no findings, say so explicitly and note any review gaps (e.g., "did not inspect transitive callees" or "could not verify shutdown ownership from the provided snippet").
Always prefer showing a real Go code fix over prose-only guidance.
Prefer minimal, concrete fixes:
ctx from struct fields to method parametersselect { case <-ctx.Done(): return } around long-running goroutine loopstime.After() in loops with a reusable time.NewTimer + timer.Reset patterndefer cancel() immediately after deriving a cancelable context (unless ownership is intentionally transferred and documented)WithValue business data with explicit parameters, typed request objects, or option structsctx instead of creating a fresh root context in library codevalue, ok := <-ch and return on !ok for channel-close shutdown semanticserrgroup.WithContext, pass the derived context (not the parent) to child goroutinesKeep fix examples tightly scoped to the finding — do not rewrite the entire function.
type Service struct {
ctx context.Context
db *sql.DB
}
At least Medium risk, often High if the struct outlives a single request. The request's cancellation becomes hidden object state and can be reused incorrectly across calls. Fix: pass ctx as a method parameter.
go func() {
for msg := range jobs {
process(msg)
}
}()
If the goroutine is tied to request work or a derived context, flag the missing cancellation path. Fix: add a select that honors ctx.Done().
ctx = context.WithValue(ctx, userIDKey, userID)
Flag when userID is a required business input. Cross-cutting metadata like trace IDs is acceptable. Fix: pass userID as an explicit function parameter.
for {
select {
case msg := <-ch:
handle(msg)
case <-time.After(5 * time.Second):
flush()
}
}
Each loop iteration allocates a new timer that cannot be GC'd until it fires. Under high throughput this leaks memory. Fix: use time.NewTicker or time.NewTimer with Reset.