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

FastAPI Lifespan Context Manager Async Resource Teardown and Worker Drain

Enforces deterministic resource lifecycle management using FastAPI's asynccontextmanager lifespan protocol instead of deprecated @app.on_event handlers. Guarantees structured teardown of async connection pools, active task queues, and external clients within a protected try/finally block during SIGTERM or application termination.

Prohibited Anti-Pattern: Leaking unclosed connection pools and dropping active in-flight worker tasks via legacy @app.on_event handlers

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: FastAPI Lifespan Context Manager Async Resource Teardown and Worker Drain - Enforces deterministic resource lifecycle management using FastAPI's asynccontextmanager lifespan protocol instead of deprecated @app.on_event handlers. Guarantees structured teardown of async connection pools, active task queues, and external clients within a protected try/finally block during SIGTERM or application termination.
globs: **/*.py, app/**/*.py, src/**/*.py
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/fastapi-lifespan-async-resource-teardown"
---

# FastAPI Lifespan Context Manager Async Resource Teardown and Worker Drain

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Leaking unclosed connection pools and dropping active in-flight worker tasks via legacy @app.on_event handlers

## System Constraints
You are a Principal Software Architect enforcing production-grade resource lifecycle invariants in FastAPI applications. You strictly prohibit deprecated '@app.on_event("startup")' and '@app.on_event("shutdown")' decorators. You mandate that all stateful resources—including database engines (SQLAlchemy, Tortoise), Redis connection pools, HTTP client sessions (httpx), background task workers, and telemetry exporters—are initialized and deterministically torn down using an '@asynccontextmanager' lifespan handler on the FastAPI instance. You enforce strict 'try...finally' blocks within the lifespan context to ensure clean shutdown, connection pool flushing, and task draining during SIGTERM events in containerized environments (Kubernetes, ECS).

## Strict Guidelines
- Engineers MUST ALWAYS use the '@asynccontextmanager' lifespan parameter on the 'FastAPI' instance instead of legacy '@app.on_event("startup")' and '@app.on_event("shutdown")' hooks.
- Engineers MUST ALWAYS enclose post-yield resource deallocations in a 'finally' block within the lifespan function to ensure teardown runs even on unhandled startup or runtime exceptions.
- Engineers MUST yield an explicit dictionary or typed mapping from the lifespan manager to populate 'app.state' safely without mutating module-level global singletons.
- Engineers MUST cleanly await pool disposals (e.g., 'await engine.dispose()', 'await redis.aclose()', 'await client.aclose()') and cancel/drain pending background workers before process exit.
- Engineers MUST NOT initialize asynchronous resource connections outside the lifespan context or in the global module import scope.

## Bad vs Good Implementations
### Migrating Deprecated Startup/Shutdown Events to Lifespan Context Manager with Deterministic Teardown

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Using '@app.on_event' is deprecated since FastAPI 0.93.0 and Starlette 0.26.0. It exposes global mutable singletons, offers no structured concurrency guarantees, and fails to handle partial initialization crashes or clean resource disposal if an earlier shutdown step raises an unhandled exception.
```python
from fastapi import FastAPI
import httpx
import redis.asyncio as aioredis
from sqlalchemy.ext.asyncio import create_async_engine

app = FastAPI()

# Anti-pattern: Global mutable state and deprecated uncoordinated lifecycle events
engine = None
redis_pool = None
http_client = None

@app.on_event("startup")
async def startup_event() -> None:
    global engine, redis_pool, http_client
    engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
    redis_pool = aioredis.from_url("redis://localhost:6379")
    http_client = httpx.AsyncClient()

@app.on_event("shutdown")
async def shutdown_event() -> None:
    # Anti-pattern: If redis fails to close, http_client and engine.dispose leak.
    # Lacks try/finally coordination and context-driven state propagation.
    await redis_pool.aclose()
    await http_client.aclose()
    await engine.dispose()
```

#### Production Standard (Correct)
> **Standard**: Adheres to FastAPI's asynccontextmanager lifespan standard. Resources are scoped, mapped to typed state, and protected by nested 'finally' blocks to guarantee that all pools (HTTP, Redis, SQLAlchemy) are deterministically closed during SIGTERM even if an adjacent teardown operation raises an exception.
```python
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from typing import TypedDict
from fastapi import FastAPI
import httpx
import redis.asyncio as aioredis
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine

class AppState(TypedDict):
    engine: AsyncEngine
    redis: aioredis.Redis
    http_client: httpx.AsyncClient

@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncIterator[AppState]:
    # Phase 1: Resource Allocation
    engine: AsyncEngine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db", pool_pre_ping=True)
    redis: aioredis.Redis = aioredis.from_url("redis://localhost:6379", decode_responses=True)
    http_client: httpx.AsyncClient = httpx.AsyncClient(timeout=10.0)

    try:
        yield {
            "engine": engine,
            "redis": redis,
            "http_client": http_client
        }
    finally:
        # Phase 2: Deterministic Teardown via structured exception handling
        # Each resource closes independently to prevent cascaded teardown failures
        try:
            await http_client.aclose()
        finally:
            try:
                await redis.aclose()
            finally:
                await engine.dispose()

app = FastAPI(lifespan=lifespan)
```

Recommended MCP Servers (mcp-serverlist.com)

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

Architectural Specification & Invariant Rationale

Architectural Invariant Design: FastAPI Lifespan Context Manager Async Resource Teardown and Worker Drain

FastAPI Lifespan Context Manager Async Resource Teardown and Worker Drain defines an industrial-grade behavioral standard for backend engineering. Enforces deterministic resource lifecycle management using FastAPI's asynccontextmanager lifespan protocol instead of deprecated @app.on_event handlers. Guarantees structured teardown of async connection pools, active task queues, and external clients within a protected try/finally block during SIGTERM or application termination. 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: Leaking unclosed connection pools and dropping active in-flight worker tasks via legacy @app.on_event handlers 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 DriftLeaking unclosed connection pools and dropping active in-flight worker tasks via legacy @app.on_event handlersCritical
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' lifespan parameter on the 'FastAPI' instance instead of legacy '@app.on_event("startup")' and '@app.on_event("shutdown")' hooks.

Engineers MUST ALWAYS use the '@asynccontextmanager' lifespan parameter on the 'FastAPI' instance instead of legacy '@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 ALWAYS enclose post-yield resource deallocations in a 'finally' block within the lifespan function to ensure teardown runs even on unhandled startup or runtime exceptions.

Engineers MUST ALWAYS enclose post-yield resource deallocations in a 'finally' block within the lifespan function to ensure teardown runs even on unhandled startup or runtime exceptions.

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

Engineers MUST yield an explicit dictionary or typed mapping from the lifespan manager to populate 'app.state' safely without mutating module-level global singletons.

Engineers MUST yield an explicit dictionary or typed mapping from the lifespan manager to populate 'app.state' safely without mutating module-level global singletons.

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

Engineers MUST cleanly await pool disposals (e.g., 'await engine.dispose()', 'await redis.aclose()', 'await client.aclose()') and cancel/drain pending background workers before process exit.

Engineers MUST cleanly await pool disposals (e.g., 'await engine.dispose()', 'await redis.aclose()', 'await client.aclose()') and cancel/drain pending background workers before process 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.
5

Engineers MUST NOT initialize asynchronous resource connections outside the lifespan context or in the global module import scope.

Engineers MUST NOT initialize asynchronous resource connections outside the lifespan context or in the global module import scope.

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 Lifespan Context Manager Async Resource Teardown and Worker Drain critical for AI coding assistants?

Enforces deterministic resource lifecycle management using FastAPI's asynccontextmanager lifespan protocol instead of deprecated @app.on_event handlers. Guarantees structured teardown of async connection pools, active task queues, and external clients within a protected try/finally block during SIGTERM or application termination. 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 (Leaking unclosed connection pools and dropping active in-flight worker tasks via legacy @app.on_event handlers) 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