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

FastAPI AnyIO Structured TaskGroup Exception Shielding Invariant

Enforces strict structured concurrency via AnyIO TaskGroups within FastAPI asynchronous execution contexts to guarantee deterministic cancellation propagation. Prevents orphaned background tasks and resource leaks by mandating explicit cancellation scopes and shielding critical cleanup blocks during task group failure.

Prohibited Anti-Pattern: Unstructured coroutine spawning via asyncio.create_task or unshielded cancellation blocks leaking zombie background workers.

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: FastAPI AnyIO Structured TaskGroup Exception Shielding Invariant - Enforces strict structured concurrency via AnyIO TaskGroups within FastAPI asynchronous execution contexts to guarantee deterministic cancellation propagation. Prevents orphaned background tasks and resource leaks by mandating explicit cancellation scopes and shielding critical cleanup blocks during task group failure.
globs: src/**/*.py, app/**/*.py, routers/**/*.py, services/**/*.py
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/fastapi-anyio-taskgroup-shielding-invariant"
---

# FastAPI AnyIO Structured TaskGroup Exception Shielding Invariant

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Unstructured coroutine spawning via asyncio.create_task or unshielded cancellation blocks leaking zombie background workers.

## System Constraints
You are a Principal Software Architect enforcing the FastAPI AnyIO Structured TaskGroup Exception Shielding Invariant. You must reject unstructured coroutine scheduling (e.g., asyncio.create_task, asyncio.gather without strict TaskGroups) inside FastAPI endpoints and background service calls. You must ensure all concurrent subtasks run inside structured `anyio.create_task_group()` contexts, propagate BaseException/CancelledError faithfully, and wrap all transactional rollbacks or cleanup inside `anyio.CancelScope(shield=True)` to prevent orphaned background tasks and partial state mutations.

## Strict Guidelines
- Engineers MUST ALWAYS use `anyio.create_task_group()` instead of `asyncio.create_task()` or naked `asyncio.gather()` when fanning out asynchronous subtasks in FastAPI dependencies, routes, or services.
- Engineers MUST wrap all critical cleanup, compensation, or database rollback logic inside an `anyio.CancelScope(shield=True)` context when executing within or around an active TaskGroup cancellation.
- Engineers MUST NOT catch `anyio.get_cancelled_exc_class()` or `asyncio.CancelledError` without immediately re-raising, unless explicitly completing an idempotent terminal teardown within a shielded scope.
- Code SHOULD catch `ExceptionGroup` (or `BaseExceptionGroup` in Python 3.11+) when unwinding multi-task failures from a TaskGroup exit, preserving the causal exception chain.
- Services MUST ensure that if any concurrent subtask fails, all sibling subtasks in the task group are cancelled immediately and awaited deterministically before the request lifecycle terminates.

## Bad vs Good Implementations
### Unstructured Fire-and-Forget vs Structured AnyIO TaskGroup with Shielded Cleanup

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Using asyncio.create_task or unconstrained asyncio.gather leaves sibling coroutines running as detached background zombies when one task encounters an error. If the client disconnects or billing fails, inventory reservation keeps running to completion without cancellation, causing data inconsistency and resource leaks.
```python
import asyncio
from fastapi import APIRouter, HTTPException

router = APIRouter()

async def bill_customer(user_id: str) -> None:
    await asyncio.sleep(0.5)
    raise RuntimeError("Payment gateway timeout")

async def reserve_inventory(item_id: str) -> None:
    await asyncio.sleep(2.0)
    # This continues running orphaned in the background if billing fails
    print("Inventory reserved permanently")

@router.post("/checkout")
async def checkout(user_id: str, item_id: str):
    # ANTI-PATTERN: Raw asyncio.gather does not cancel lingering tasks on failure
    # and can leak running coroutines when a client disconnects.
    try:
        task1 = asyncio.create_task(bill_customer(user_id))
        task2 = asyncio.create_task(reserve_inventory(item_id))
        await asyncio.gather(task1, task2)
    except Exception as exc:
        # Failed to cancel task2; task2 continues executing as a zombie task
        raise HTTPException(status_code=500, detail=str(exc))
```

#### Production Standard (Correct)
> **Standard**: Using anyio.create_task_group guarantees structured concurrency: if bill_customer raises a RuntimeError, reserve_inventory is immediately cancelled and awaited. The cleanup operation in reserve_inventory is shielded via anyio.CancelScope(shield=True), guaranteeing that compensating transactions finish cleanly without leaking zombie tasks.
```python
import anyio
from fastapi import APIRouter, HTTPException

router = APIRouter()

async def bill_customer(user_id: str) -> None:
    await anyio.sleep(0.5)
    raise RuntimeError("Payment gateway timeout")

async def reserve_inventory(item_id: str) -> None:
    try:
        await anyio.sleep(2.0)
    except anyio.get_cancelled_exc_class():
        # Must use shielded scope for rollback to avoid interruption during cancellation
        with anyio.CancelScope(shield=True):
            await release_inventory_reservation(item_id)
        raise

async def release_inventory_reservation(item_id: str) -> None:
    # Guaranteed cleanup under shield
    await anyio.sleep(0.1)

@router.post("/checkout")
async def checkout(user_id: str, item_id: str):
    try:
        async with anyio.create_task_group() as tg:
            tg.start_soon(bill_customer, user_id)
            tg.start_soon(reserve_inventory, item_id)
    except* RuntimeError as eg:
        # Structured cancellation propagated; reserve_inventory was cancelled and cleaned up
        raise HTTPException(status_code=502, detail=f"Checkout failed: {eg.exceptions}")
```

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 AnyIO Structured TaskGroup Exception Shielding Invariant

FastAPI AnyIO Structured TaskGroup Exception Shielding Invariant defines an industrial-grade behavioral standard for backend engineering. Enforces strict structured concurrency via AnyIO TaskGroups within FastAPI asynchronous execution contexts to guarantee deterministic cancellation propagation. Prevents orphaned background tasks and resource leaks by mandating explicit cancellation scopes and shielding critical cleanup blocks during task group failure. 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 coroutine spawning via asyncio.create_task or unshielded cancellation blocks leaking zombie background workers. 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 coroutine spawning via asyncio.create_task or unshielded cancellation blocks leaking zombie background workers.Critical
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 `anyio.create_task_group()` instead of `asyncio.create_task()` or naked `asyncio.gather()` when fanning out asynchronous subtasks in FastAPI dependencies, routes, or services.

Engineers MUST ALWAYS use `anyio.create_task_group()` instead of `asyncio.create_task()` or naked `asyncio.gather()` when fanning out asynchronous subtasks in FastAPI dependencies, routes, or services.

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 all critical cleanup, compensation, or database rollback logic inside an `anyio.CancelScope(shield=True)` context when executing within or around an active TaskGroup cancellation.

Engineers MUST wrap all critical cleanup, compensation, or database rollback logic inside an `anyio.CancelScope(shield=True)` context when executing within or around an active TaskGroup cancellation.

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 NOT catch `anyio.get_cancelled_exc_class()` or `asyncio.CancelledError` without immediately re-raising, unless explicitly completing an idempotent terminal teardown within a shielded scope.

Engineers MUST NOT catch `anyio.get_cancelled_exc_class()` or `asyncio.CancelledError` without immediately re-raising, unless explicitly completing an idempotent terminal teardown within a shielded 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.
4

Code SHOULD catch `ExceptionGroup` (or `BaseExceptionGroup` in Python 3.11+) when unwinding multi-task failures from a TaskGroup exit, preserving the causal exception chain.

Code SHOULD catch `ExceptionGroup` (or `BaseExceptionGroup` in Python 3.11+) when unwinding multi-task failures from a TaskGroup exit, preserving the causal exception chain.

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

Services MUST ensure that if any concurrent subtask fails, all sibling subtasks in the task group are cancelled immediately and awaited deterministically before the request lifecycle terminates.

Services MUST ensure that if any concurrent subtask fails, all sibling subtasks in the task group are cancelled immediately and awaited deterministically before the request lifecycle terminates.

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

Enforces strict structured concurrency via AnyIO TaskGroups within FastAPI asynchronous execution contexts to guarantee deterministic cancellation propagation. Prevents orphaned background tasks and resource leaks by mandating explicit cancellation scopes and shielding critical cleanup blocks during task group failure. 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 coroutine spawning via asyncio.create_task or unshielded cancellation blocks leaking zombie background workers.) 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