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

FastAPI Async Lifespan Graceful Drainage & Connection Teardown Guardrail

Enforces deterministic termination of persistent resource pools (databases, HTTP clients, message brokers) and background coroutine task groups during ASGI lifespan shutdown. Guarantees zero dropped in-flight requests during SIGTERM orchestrations by decoupling server draining from abrupt pool termination.

Prohibited Anti-Pattern: Premature Pool Disconnection or Zombie Background Tasks in Async Lifespan Teardown

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: FastAPI Async Lifespan Graceful Drainage & Connection Teardown Guardrail - Enforces deterministic termination of persistent resource pools (databases, HTTP clients, message brokers) and background coroutine task groups during ASGI lifespan shutdown. Guarantees zero dropped in-flight requests during SIGTERM orchestrations by decoupling server draining from abrupt pool termination.
globs: **/app.py, **/main.py, **/lifespan.py, **/*lifespan*.py, **/core/config.py
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/fastapi-async-lifespan-drainage-guardrail"
---

# FastAPI Async Lifespan Graceful Drainage & Connection Teardown Guardrail

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Premature Pool Disconnection or Zombie Background Tasks in Async Lifespan Teardown

## System Constraints
You are a Principal Backend Architect enforcing rock-solid async lifecycles in FastAPI applications. Your objective is to mandate strict lifespan context managers, explicit task cancellation with timeout thresholds, and ordered teardown of persistent network clients without terminating resources while ASGI in-flight requests are resolving.

## Strict Guidelines
- Engineers MUST use modern `lifespan` async context managers on `FastAPI(lifespan=...)` instead of deprecated `@app.on_event('startup')` or `@app.on_event('shutdown')` decorators.
- Engineers MUST instantiate shared persistent clients (e.g., `httpx.AsyncClient`, `asyncpg.Pool`, `redis.asyncio.Redis`) inside the lifespan context and expose them through `app.state`, not global mutable module variables.
- Engineers MUST wrap long-running or detached background coroutines inside an `asyncio.TaskGroup` or manage their cancellation via an explicit `asyncio.gather(*tasks, return_exceptions=True)` during lifespan exit.
- Lifespan exit blocks MUST implement a structured timeout via `asyncio.timeout()` or `asyncio.wait_for()` to prevent hung socket connections from deadlocking Kubernetes SIGTERM grace periods.
- Engineers MUST NOT close connection pools prior to completing pending background tasks that depend on those connection pools.
- Code MUST handle `asyncio.CancelledError` cooperatively within all background loops and execute cleanup logic inside `finally` blocks.

## Bad vs Good Implementations
### Graceful Pool Drainage and Coroutine Scoping in FastAPI Lifespan

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Uses deprecated on_event hooks, mutates global state, fails to gracefully drain or cancel the running background task, and closes httpx.AsyncClient immediately which causes active in-flight requests to fail abruptly with runtime client exceptions during pod termination.
```python
import asyncio
from fastapi import FastAPI
import httpx

app = FastAPI()
client: httpx.AsyncClient = None
background_tasks = []

@app.on_event("startup")
async def startup():
    global client
    client = httpx.AsyncClient()
    # Anti-pattern: Untracked background task without structured cancellation
    background_tasks.append(asyncio.create_task(run_poller()))

@app.on_event("shutdown")
async def shutdown():
    # Anti-pattern: Closes client immediately; in-flight ASGI requests crash with ClientClosed
    await client.aclose()

async def run_poller():
    while True:
        await asyncio.sleep(5)
        await client.get("https://api.internal/metrics")
```

#### Production Standard (Correct)
> **Standard**: Adheres to ASGI lifespan contract. Persistent dependencies reside in app.state. During teardown, background coroutines are cleanly cancelled and awaited with an explicit timeout before persistent connection pools are closed, preventing in-flight drop-offs and hung shutdown procedures.
```python
import asyncio
from contextlib import asynccontextmanager
from typing import AsyncGenerator
from fastapi import FastAPI, Request
import httpx

async def run_worker(client: httpx.AsyncClient) -> None:
    try:
        while True:
            await asyncio.sleep(5)
            # Safely perform periodic reconciliation
            await client.get("https://api.internal/metrics")
    except asyncio.CancelledError:
        # Cooperative cancellation handled cleanly
        pass

@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
    # 1. Startup phase: allocate shared pools
    client = httpx.AsyncClient(timeout=10.0)
    app.state.http_client = client
    worker_task = asyncio.create_task(run_worker(client))

    try:
        # Yield execution to ASGI server to process requests
        yield
    finally:
        # 2. Teardown phase: triggered only after ASGI server stops accepting new traffic
        # First, cancel detached background workers
        worker_task.cancel()
        try:
            async with asyncio.timeout(5.0):
                await worker_task
        except (TimeoutError, asyncio.CancelledError):
            pass

        # Second, drain and shut down connection pools
        async with asyncio.timeout(5.0):
            await client.aclose()

app = FastAPI(lifespan=lifespan)

@app.get("/data")
async def get_data(request: Request) -> dict:
    resp = await request.app.state.http_client.get("https://api.internal/data")
    return resp.json()
```

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 Graceful Drainage & Connection Teardown Guardrail

FastAPI Async Lifespan Graceful Drainage & Connection Teardown Guardrail defines an industrial-grade behavioral standard for backend engineering. Enforces deterministic termination of persistent resource pools (databases, HTTP clients, message brokers) and background coroutine task groups during ASGI lifespan shutdown. Guarantees zero dropped in-flight requests during SIGTERM orchestrations by decoupling server draining from abrupt pool 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: Premature Pool Disconnection or Zombie Background Tasks in Async Lifespan Teardown 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 DriftPremature Pool Disconnection or Zombie Background Tasks in Async Lifespan TeardownCritical
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 use modern `lifespan` async context managers on `FastAPI(lifespan=...)` instead of deprecated `@app.on_event('startup')` or `@app.on_event('shutdown')` decorators.

Engineers MUST use modern `lifespan` async context managers on `FastAPI(lifespan=...)` instead of deprecated `@app.on_event('startup')` or `@app.on_event('shutdown')` decorators.

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 instantiate shared persistent clients (e.g., `httpx.AsyncClient`, `asyncpg.Pool`, `redis.asyncio.Redis`) inside the lifespan context and expose them through `app.state`, not global mutable module variables.

Engineers MUST instantiate shared persistent clients (e.g., `httpx.AsyncClient`, `asyncpg.Pool`, `redis.asyncio.Redis`) inside the lifespan context and expose them through `app.state`, not global mutable module variables.

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 wrap long-running or detached background coroutines inside an `asyncio.TaskGroup` or manage their cancellation via an explicit `asyncio.gather(*tasks, return_exceptions=True)` during lifespan exit.

Engineers MUST wrap long-running or detached background coroutines inside an `asyncio.TaskGroup` or manage their cancellation via an explicit `asyncio.gather(*tasks, return_exceptions=True)` during lifespan 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.
4

Lifespan exit blocks MUST implement a structured timeout via `asyncio.timeout()` or `asyncio.wait_for()` to prevent hung socket connections from deadlocking Kubernetes SIGTERM grace periods.

Lifespan exit blocks MUST implement a structured timeout via `asyncio.timeout()` or `asyncio.wait_for()` to prevent hung socket connections from deadlocking Kubernetes SIGTERM grace 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.
5

Engineers MUST NOT close connection pools prior to completing pending background tasks that depend on those connection pools.

Engineers MUST NOT close connection pools prior to completing pending background tasks that depend on those connection pools.

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.
6

Code MUST handle `asyncio.CancelledError` cooperatively within all background loops and execute cleanup logic inside `finally` blocks.

Code MUST handle `asyncio.CancelledError` cooperatively within all background loops and execute cleanup logic inside `finally` blocks.

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 Graceful Drainage & Connection Teardown Guardrail critical for AI coding assistants?

Enforces deterministic termination of persistent resource pools (databases, HTTP clients, message brokers) and background coroutine task groups during ASGI lifespan shutdown. Guarantees zero dropped in-flight requests during SIGTERM orchestrations by decoupling server draining from abrupt pool 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 (Premature Pool Disconnection or Zombie Background Tasks in Async Lifespan Teardown) 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