This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
backendcritical PriorityID: go-context-afterfunc-deregistration-leak-guardrail

Go 1.24 context.AfterFunc Explicit Teardown & Closure Deregistration Guardrail

Enforces mandatory, unconditional invocation of the returned stop closure from context.AfterFunc in distributed RPC pipelines and stream handlers. Failure to deregister callbacks on short-lived operations tied to long-lived contexts causes unbounded callback list growth, memory retention of captured references, and goroutine starvation under high throughput.

Prohibited Anti-Pattern: Omitting AfterFunc Stop Closure Invocations Leading to Retained Callbacks on Long-Lived Contexts

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Go 1.24 context.AfterFunc Explicit Teardown & Closure Deregistration Guardrail - Enforces mandatory, unconditional invocation of the returned stop closure from context.AfterFunc in distributed RPC pipelines and stream handlers. Failure to deregister callbacks on short-lived operations tied to long-lived contexts causes unbounded callback list growth, memory retention of captured references, and goroutine starvation under high throughput.
globs: **/*.go
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/go-context-afterfunc-deregistration-leak-guardrail"
---

# Go 1.24 context.AfterFunc Explicit Teardown & Closure Deregistration Guardrail

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Omitting AfterFunc Stop Closure Invocations Leading to Retained Callbacks on Long-Lived Contexts

## System Constraints
You are a Principal Systems Architect enforcing Go 1.24 context invariants. In long-running distributed pipelines (such as gRPC streams, message queues, and continuous workers), context.AfterFunc must never be registered without immediate, deferred deregistration via its returned stop closure. Furthermore, closures passed to context.AfterFunc must never retain large structures or mutate uncoordinated state after caller return. Mandate deterministic cleanup, zero unpinned closure references, and synchronization when the callback may already be in flight.

## Strict Guidelines
- Engineers MUST ALWAYS immediately capture and defer the `stop` function returned by `context.AfterFunc` to guarantee deregistration upon function exit.
- Engineers MUST NOT discard the returned cleanup function from `context.AfterFunc` (`_ = context.AfterFunc(...)` is strictly forbidden).
- Closures passed to `context.AfterFunc` MUST NOT close over heap-allocated request payloads, large byte buffers, or long-lived connection structs; only pass minimal primitive handles or lightweight synchronization primitives.
- Services MUST implement deterministic synchronization (e.g., sync.WaitGroup or atomic transitions) if the callback accesses resources that are freed at the end of the enclosing scope when `stop()` returns false.
- Components NEVER attach `context.AfterFunc` callbacks to parent process or application-level root contexts without a deterministic unregistration path matching the lifecycle of the child operation.

## Bad vs Good Implementations
### Registration and Teardown of context.AfterFunc in RPC Request Handling

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Ignoring the return function of context.AfterFunc leaks the closure into the parent context's internal tree. In streaming or multiplexed RPCs, thousands of abandoned closures accumulate, retaining the 1MB payload per invocation until the root context terminates.
```go
package rpc

import (
	"context"
	"net/http"
)

type LargePayload struct {
	Data [1 << 20]byte
}

func HandleStreamItem(ctx context.Context, client *http.Client, req *LargePayload) error {
	// ANTI-PATTERN: Return value ignored. If ctx lives for the duration of a long-lived stream
	// or connection, this closure and req will remain pinned in memory indefinitely.
	context.AfterFunc(ctx, func() {
		client.CloseIdleConnections()
		_ = req.Data[0] // Retains entire 1MB payload in closure environment
	})

	return doRequest(client, req)
}

func doRequest(client *http.Client, req *LargePayload) error {
	return nil
}
```

#### Production Standard (Correct)
> **Standard**: The stop closure is captured and invoked via defer. If stop() returns false, the callback is already running concurrently, so sync.WaitGroup guarantees memory and resource safety before HandleStreamItem returns. The payload is not captured inside the closure.
```go
package rpc

import (
	"context"
	"net/http"
	"sync"
)

type LargePayload struct {
	Data [1 << 20]byte
}

func HandleStreamItem(ctx context.Context, client *http.Client, req *LargePayload) error {
	var wg sync.WaitGroup
	wg.Add(1)

	// GOOD: Capture minimal dependencies, assign stop func, and defer execution
	stop := context.AfterFunc(ctx, func() {
		defer wg.Done()
		client.CloseIdleConnections()
	})

	defer func() {
		if !stop() {
			// Callback has started or already completed; wait for concurrent execution to finish
			wg.Wait()
		} else {
			// Callback successfully stopped before execution; decrement counter
			wg.Done()
		}
	}()

	return doRequest(client, req)
}

func doRequest(client *http.Client, req *LargePayload) error {
	return nil
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Go 1.24 context.AfterFunc Explicit Teardown & Closure Deregistration Guardrail

Go 1.24 context.AfterFunc Explicit Teardown & Closure Deregistration Guardrail defines an industrial-grade behavioral standard for backend engineering. Enforces mandatory, unconditional invocation of the returned stop closure from context.AfterFunc in distributed RPC pipelines and stream handlers. Failure to deregister callbacks on short-lived operations tied to long-lived contexts causes unbounded callback list growth, memory retention of captured references, and goroutine starvation under high throughput. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Omitting AfterFunc Stop Closure Invocations Leading to Retained Callbacks on Long-Lived Contexts Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftOmitting AfterFunc Stop Closure Invocations Leading to Retained Callbacks on Long-Lived ContextsCritical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS immediately capture and defer the `stop` function returned by `context.AfterFunc` to guarantee deregistration upon function exit.

Engineers MUST ALWAYS immediately capture and defer the `stop` function returned by `context.AfterFunc` to guarantee deregistration upon function exit.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST NOT discard the returned cleanup function from `context.AfterFunc` (`_ = context.AfterFunc(...)` is strictly forbidden).

Engineers MUST NOT discard the returned cleanup function from `context.AfterFunc` (`_ = context.AfterFunc(...)` is strictly forbidden).

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Closures passed to `context.AfterFunc` MUST NOT close over heap-allocated request payloads, large byte buffers, or long-lived connection structs

Closures passed to `context.AfterFunc` MUST NOT close over heap-allocated request payloads, large byte buffers, or long-lived connection structs; only pass minimal primitive handles or lightweight synchronization primitives.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Services MUST implement deterministic synchronization (e.g., sync.WaitGroup or atomic transitions) if the callback accesses resources that are freed at the end of the enclosing scope when `stop()` returns false.

Services MUST implement deterministic synchronization (e.g., sync.WaitGroup or atomic transitions) if the callback accesses resources that are freed at the end of the enclosing scope when `stop()` returns false.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

Components NEVER attach `context.AfterFunc` callbacks to parent process or application-level root contexts without a deterministic unregistration path matching the lifecycle of the child operation.

Components NEVER attach `context.AfterFunc` callbacks to parent process or application-level root contexts without a deterministic unregistration path matching the lifecycle of the child operation.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is Go 1.24 context.AfterFunc Explicit Teardown & Closure Deregistration Guardrail critical for AI coding assistants?

Enforces mandatory, unconditional invocation of the returned stop closure from context.AfterFunc in distributed RPC pipelines and stream handlers. Failure to deregister callbacks on short-lived operations tied to long-lived contexts causes unbounded callback list growth, memory retention of captured references, and goroutine starvation under high throughput. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Omitting AfterFunc Stop Closure Invocations Leading to Retained Callbacks on Long-Lived Contexts) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

1 Rule in Stack

Go