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

FastAPI Lifespan Structured TaskGroup Teardown Invariant

Enforces structured concurrency inside ASGI lifespan context managers using anyio or asyncio TaskGroups. Prevents unmanaged fire-and-forget background tasks from leaking connections, corrupting state, or abruptly terminating upon SIGTERM by guaranteeing deterministic cancellation and resource draining.

Prohibited Anti-Pattern: Spawning Unbounded Fire-and-Forget asyncio.create_task Inside Lifespans

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: FastAPI Lifespan Structured TaskGroup Teardown Invariant - Enforces structured concurrency inside ASGI lifespan context managers using anyio or asyncio TaskGroups. Prevents unmanaged fire-and-forget background tasks from leaking connections, corrupting state, or abruptly terminating upon SIGTERM by guaranteeing deterministic cancellation and resource draining.
globs: app/**/*.py, src/**/*.py, main.py
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/fastapi-lifespan-taskgroup-teardown-invariant"
---

# FastAPI Lifespan Structured TaskGroup Teardown Invariant

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Spawning Unbounded Fire-and-Forget asyncio.create_task Inside Lifespans

## System Constraints
You are a Principal Backend Architect enforcing strict structured concurrency in ASGI applications. Mandate that all asynchronous background workers, telemetry daemons, and consumer loops initiated during FastAPI application startup are scoped strictly within an anyio or asyncio.TaskGroup inside the lifespan generator. Prohibit bare asyncio.create_task calls that escape lifecycle management.

## Strict Guidelines
- Engineers MUST ALWAYS encapsulate concurrent background tasks within an explicit anyio.create_task_group() or Python 3.11+ asyncio.TaskGroup() block in the ASGI lifespan context manager.
- Engineers MUST NOT spawn detached background tasks using asyncio.create_task() or loop.create_task() inside lifespan handlers or API routes without an explicit lifecycle owner.
- Code MUST use a cooperative cancellation mechanism (such as anyio.Event or asyncio.Event) to signal workers to drain before exiting the lifespan context block.
- Services MUST ensure that the lifespan generator yields execution to FastAPI strictly while the task group context remains open, awaiting complete task completion upon teardown.
- Components NEVER swallow or suppress asyncio.CancelledError or anyio.get_cancelled_exc_class() without re-raising or executing required cleanup.

## Bad vs Good Implementations
### Unbounded Background Task vs. Structured Lifespan TaskGroup

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Spawning tasks with asyncio.create_task inside the lifespan creates detached coroutines that are not tracked or drained when the application receives SIGTERM. The ASGI container shuts down immediately after yield, causing abrupt task truncation, unclosed database transactions, and leaked connections.
```python
import asyncio
from contextlib import asynccontextmanager
from fastapi import FastAPI

async def background_worker():
    while True:
        await asyncio.sleep(1)
        # Process queue items

@asynccontextmanager
async def lifespan(app: FastAPI):
    # ANTI-PATTERN: Fire-and-forget task escapes lifecycle control
    # On SIGTERM, Uvicorn terminates the event loop before worker cleans up,
    # causing leaked DB connections or half-committed writes.
    asyncio.create_task(background_worker())
    yield

app = FastAPI(lifespan=lifespan)
```

#### Production Standard (Correct)
> **Standard**: Encapsulating the background worker inside an anyio.create_task_group() within the lifespan generator guarantees structured concurrency. When the ASGI host initiates shutdown, the finally block signals the worker and cancels the task scope, ensuring all coroutines drain and complete cleanup before the lifespan exit returns.
```python
from contextlib import asynccontextmanager
import anyio
from fastapi import FastAPI

async def background_worker(stop_event: anyio.Event):
    try:
        while not stop_event.is_set():
            # Simulate cooperative batch processing
            with anyio.move_on_after(1.0):
                await stop_event.wait()
    finally:
        # Deterministic teardown and state flushing
        await anyio.to_thread.run_sync(lambda: None)

@asynccontextmanager
async def lifespan(app: FastAPI):
    stop_event = anyio.Event()
    async with anyio.create_task_group() as tg:
        tg.start_soon(background_worker, stop_event)
        try:
            yield
        finally:
            # Signal cooperative termination and allow tg to drain and cancel
            stop_event.set()
            tg.cancel_scope.cancel()

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 Lifespan Structured TaskGroup Teardown Invariant

FastAPI Lifespan Structured TaskGroup Teardown Invariant defines an industrial-grade behavioral standard for backend engineering. Enforces structured concurrency inside ASGI lifespan context managers using anyio or asyncio TaskGroups. Prevents unmanaged fire-and-forget background tasks from leaking connections, corrupting state, or abruptly terminating upon SIGTERM by guaranteeing deterministic cancellation and resource draining. 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: Spawning Unbounded Fire-and-Forget asyncio.create_task Inside Lifespans 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 DriftSpawning Unbounded Fire-and-Forget asyncio.create_task Inside LifespansCritical
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 encapsulate concurrent background tasks within an explicit anyio.create_task_group() or Python 3.11+ asyncio.TaskGroup() block in the ASGI lifespan context manager.

Engineers MUST ALWAYS encapsulate concurrent background tasks within an explicit anyio.create_task_group() or Python 3.11+ asyncio.TaskGroup() block in the ASGI lifespan context manager.

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 spawn detached background tasks using asyncio.create_task() or loop.create_task() inside lifespan handlers or API routes without an explicit lifecycle owner.

Engineers MUST NOT spawn detached background tasks using asyncio.create_task() or loop.create_task() inside lifespan handlers or API routes without an explicit lifecycle owner.

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 MUST use a cooperative cancellation mechanism (such as anyio.Event or asyncio.Event) to signal workers to drain before exiting the lifespan context block.

Code MUST use a cooperative cancellation mechanism (such as anyio.Event or asyncio.Event) to signal workers to drain before exiting the lifespan context block.

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 ensure that the lifespan generator yields execution to FastAPI strictly while the task group context remains open, awaiting complete task completion upon teardown.

Services MUST ensure that the lifespan generator yields execution to FastAPI strictly while the task group context remains open, awaiting complete task completion upon teardown.

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 swallow or suppress asyncio.CancelledError or anyio.get_cancelled_exc_class() without re-raising or executing required cleanup.

Components NEVER swallow or suppress asyncio.CancelledError or anyio.get_cancelled_exc_class() without re-raising or executing required cleanup.

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 Structured TaskGroup Teardown Invariant critical for AI coding assistants?

Enforces structured concurrency inside ASGI lifespan context managers using anyio or asyncio TaskGroups. Prevents unmanaged fire-and-forget background tasks from leaking connections, corrupting state, or abruptly terminating upon SIGTERM by guaranteeing deterministic cancellation and resource draining. 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 (Spawning Unbounded Fire-and-Forget asyncio.create_task Inside Lifespans) 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