This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
backendcritical PriorityID: fastapi-async-lifespan-cleanup-invariant

FastAPI Async Lifespan Generator Deterministic Teardown Invariant

Enforces deterministic teardown of database connection pools, HTTP clients, and background task coroutines using FastAPI lifespan context managers. Guarantees clean state draining during SIGTERM signals under multi-worker ASGI runtimes (Uvicorn/Gunicorn) to prevent unhandled connection reset cascades and aborted in-flight transactions.

Prohibited Anti-Pattern: Unstructured Startup/Shutdown Handlers Leaking Sockets and Stranding Coroutines on Worker Recycling

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: FastAPI Async Lifespan Generator Deterministic Teardown Invariant - Enforces deterministic teardown of database connection pools, HTTP clients, and background task coroutines using FastAPI lifespan context managers. Guarantees clean state draining during SIGTERM signals under multi-worker ASGI runtimes (Uvicorn/Gunicorn) to prevent unhandled connection reset cascades and aborted in-flight transactions.
globs: app/**/*.py, src/**/*.py, **/main.py, **/lifespan.py
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/fastapi-async-lifespan-cleanup-invariant"
---

# FastAPI Async Lifespan Generator Deterministic Teardown Invariant

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Unstructured Startup/Shutdown Handlers Leaking Sockets and Stranding Coroutines on Worker Recycling

## System Constraints
You are a Principal Backend Architect enforcing strict asyncio lifecycle management and resource disposal patterns in FastAPI applications. Deprecate all legacy event hooks, mandate async context manager lifespan protocols, guarantee try/finally isolation around yields, and enforce deterministic cancellation and draining of spawned background tasks upon ASGI worker termination.

## Strict Guidelines
- Engineers MUST ALWAYS use the '@asynccontextmanager' protocol for FastAPI application 'lifespan' rather than deprecated '@app.on_event("startup")' and '@app.on_event("shutdown")' hooks.
- Engineers MUST wrap post-yield teardown logic within a comprehensive 'try ... finally' block to guarantee resource deallocation even if 'CancelledError' is raised during shutdown.
- Code SHOULD enforce explicit bounded timeouts (via 'asyncio.timeout' or 'asyncio.wait_for') during pool disposal to prevent zombie worker hangs during container SIGKILL escalation periods.
- Services MUST explicitly cancel, await, and catch exceptions for all long-running background tasks spawned in the lifespan context before closing persistence layers.
- Components NEVER store initialized async client state (e.g., SQLAlchemy AsyncEngine, Redis Client, httpx.AsyncClient) on global module variables; state MUST be bound to 'app.state' or managed via dependency injection registries.

## Bad vs Good Implementations
### Deterministic Lifespan Resource Teardown and Task Draining

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Legacy on_event hooks do not preserve execution context or guarantee reverse-order teardown. Background tasks are orphaned, causing unhandled TaskPendingError and abrupt TCP RST packets to downstream dependencies on worker restart.
```python
from fastapi import FastAPI
import asyncio
import httpx

app = FastAPI()

# ANTI-PATTERN: Deprecated event handlers with unmanaged global state
# Background tasks are orphaned; SIGTERM causes abrupt socket reset without pooling drain.
client = None
background_task = None

async def poll_external_worker():
    while True:
        await asyncio.sleep(5)

@app.on_event("startup")
async def startup_event():
    global client, background_task
    client = httpx.AsyncClient()
    background_task = asyncio.create_task(poll_external_worker())

@app.on_event("shutdown")
async def shutdown_event():
    # Vulnerable to unhandled CancelledError; does not await or cancel background_task
    await client.aclose()
```

#### Production Standard (Correct)
> **Standard**: Utilizes the official '@asynccontextmanager' lifespan handler with an isolated 'try ... finally' block. Background tasks are explicitly cancelled and awaited, resources bound to 'app.state' are closed in strict reverse order, and an explicit 'asyncio.timeout' bounds teardown execution before Gunicorn/Kubernetes escalates to SIGKILL.
```python
from contextlib import asynccontextmanager
from typing import AsyncGenerator
import asyncio
import logging
from fastapi import FastAPI
import httpx
from sqlalchemy.ext.asyncio import create_async_engine, AsyncEngine

logger = logging.getLogger(__name__)

async def monitor_heartbeat():
    try:
        while True:
            await asyncio.sleep(5)
    except asyncio.CancelledError:
        logger.info("Heartbeat task received cancellation; draining safely.")
        raise

@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
    # 1. Startup phase: Initialize clients and bind to app.state
    engine: AsyncEngine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db", pool_pre_ping=True)
    http_client = httpx.AsyncClient(timeout=10.0)
    app.state.db_engine = engine
    app.state.http_client = http_client

    task = asyncio.create_task(monitor_heartbeat(), name="heartbeat_monitor")
    app.state.heartbeat_task = task

    try:
        yield
    finally:
        # 2. Teardown phase: Reverse-order guaranteed teardown within bounded timeout
        logger.info("SIGTERM received: Starting deterministic resource teardown.")
        try:
            async with asyncio.timeout(10.0):
                # Cancel background tasks
                task.cancel()
                await asyncio.gather(task, return_exceptions=True)

                # Drain HTTP client connections
                await http_client.aclose()

                # Dispose SQLAlchemy database engine connection pool
                await engine.dispose()
                logger.info("Deterministic teardown completed without pool leaks.")
        except* TimeoutError:
            logger.error("Teardown timed out after 10.0s; forcing pool abandonment.")

app = FastAPI(lifespan=lifespan)
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Recommended Agent Skills (askills.dev)

Ready-to-use executable skill functions tailored to this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: FastAPI Async Lifespan Generator Deterministic Teardown Invariant

FastAPI Async Lifespan Generator Deterministic Teardown Invariant defines an industrial-grade behavioral standard for backend engineering. Enforces deterministic teardown of database connection pools, HTTP clients, and background task coroutines using FastAPI lifespan context managers. Guarantees clean state draining during SIGTERM signals under multi-worker ASGI runtimes (Uvicorn/Gunicorn) to prevent unhandled connection reset cascades and aborted in-flight transactions. 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: Unstructured Startup/Shutdown Handlers Leaking Sockets and Stranding Coroutines on Worker Recycling 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 DriftUnstructured Startup/Shutdown Handlers Leaking Sockets and Stranding Coroutines on Worker RecyclingCritical
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 use the '@asynccontextmanager' protocol for FastAPI application 'lifespan' rather than deprecated '@app.on_event("startup")' and '@app.on_event("shutdown")' hooks.

Engineers MUST ALWAYS use the '@asynccontextmanager' protocol for FastAPI application 'lifespan' rather than deprecated '@app.on_event("startup")' and '@app.on_event("shutdown")' hooks.

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 wrap post-yield teardown logic within a comprehensive 'try ... finally' block to guarantee resource deallocation even if 'CancelledError' is raised during shutdown.

Engineers MUST wrap post-yield teardown logic within a comprehensive 'try ... finally' block to guarantee resource deallocation even if 'CancelledError' is raised during shutdown.

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

Code SHOULD enforce explicit bounded timeouts (via 'asyncio.timeout' or 'asyncio.wait_for') during pool disposal to prevent zombie worker hangs during container SIGKILL escalation periods.

Code SHOULD enforce explicit bounded timeouts (via 'asyncio.timeout' or 'asyncio.wait_for') during pool disposal to prevent zombie worker hangs during container SIGKILL escalation periods.

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 explicitly cancel, await, and catch exceptions for all long-running background tasks spawned in the lifespan context before closing persistence layers.

Services MUST explicitly cancel, await, and catch exceptions for all long-running background tasks spawned in the lifespan context before closing persistence layers.

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 store initialized async client state (e.g., SQLAlchemy AsyncEngine, Redis Client, httpx.AsyncClient) on global module variables

Components NEVER store initialized async client state (e.g., SQLAlchemy AsyncEngine, Redis Client, httpx.AsyncClient) on global module variables; state MUST be bound to 'app.state' or managed via dependency injection registries.

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 FastAPI Async Lifespan Generator Deterministic Teardown Invariant critical for AI coding assistants?

Enforces deterministic teardown of database connection pools, HTTP clients, and background task coroutines using FastAPI lifespan context managers. Guarantees clean state draining during SIGTERM signals under multi-worker ASGI runtimes (Uvicorn/Gunicorn) to prevent unhandled connection reset cascades and aborted in-flight transactions. 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 (Unstructured Startup/Shutdown Handlers Leaking Sockets and Stranding Coroutines on Worker Recycling) 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

FastAPI