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

FastAPI AnyIO Structured TaskGroup Exception Propagation and Cancellation Standard

Enforces deterministic handling and explicit unrolling of multi-child ExceptionGroups across request-scoped AnyIO TaskGroups in FastAPI services. Prevents silently swallowed coroutine cancellations, hidden subtask panics, and zombie background worker states through strict isolation and shield invariants.

Prohibited Anti-Pattern: Swallowing BaseExceptions or Blindly Catching Top-Level Exception Around AnyIO TaskGroups

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: FastAPI AnyIO Structured TaskGroup Exception Propagation and Cancellation Standard - Enforces deterministic handling and explicit unrolling of multi-child ExceptionGroups across request-scoped AnyIO TaskGroups in FastAPI services. Prevents silently swallowed coroutine cancellations, hidden subtask panics, and zombie background worker states through strict isolation and shield invariants.
globs: app/**/*.py, src/**/*.py, api/**/*.py
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/fastapi-anyio-taskgroup-exception-propagation-standard"
---

# FastAPI AnyIO Structured TaskGroup Exception Propagation and Cancellation Standard

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Swallowing BaseExceptions or Blindly Catching Top-Level Exception Around AnyIO TaskGroups

## System Constraints
You are a Principal Software Architect enforcing production concurrency standards in FastAPI applications powered by AnyIO. You MUST mandate deterministic structured concurrency, strict ExceptionGroup unrolling, explicit handling of cancellation semantics, and protected resource cleanup using shielded cancel scopes.

## Strict Guidelines
- Engineers MUST ALWAYS handle multi-child exceptions raised by `anyio.create_task_group()` using Python 3.11+ `except*` syntax or explicit recursive inspection via `ExceptionGroup.subgroup()` / `split()`.
- Engineers MUST NOT use a blanket `except Exception:` block around an `anyio.create_task_group()` context without re-raising or fully dispatching all sub-exceptions contained in the `ExceptionGroup`.
- Engineers MUST NEVER catch, suppress, or mask `anyio.get_cancelled_exc_class()` (or `asyncio.CancelledError`) unless explicitly implementing idempotent cancellation cleanup followed by immediate re-raising.
- Worker coroutines scheduled via `tg.start_soon()` MUST implement granular local error boundary handling to distinguish deterministic domain failures from unhandled runtime defects before bubbling to the TaskGroup boundary.
- Teardown and persistent resource commits executed during TaskGroup cancellation MUST be wrapped inside `with anyio.CancelScope(shield=True):` to prevent premature abort of critical cleanup coroutines.
- FastAPI request-scoped background worker pools MUST map unhandled child exceptions from `ExceptionGroup` into structured telemetry events before letting the TaskGroup cancel remaining concurrent siblings.

## Bad vs Good Implementations
### Deterministic ExceptionGroup Unrolling and Cancellation Shielding

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Catching standard `Exception` around an AnyIO TaskGroup collates all concurrent child errors into a single uninspected ExceptionGroup. In Python 3.11+, this can obscure multiple distinct failures, swallow concurrent cancellation signals, or fail to propagate cancel events to siblings cleanly, leaving the client with an opaque error string and lost telemetry.
```python
import anyio
from fastapi import APIRouter, HTTPException

router = APIRouter()

async def worker_fetch(item_id: int):
    if item_id == 2:
        raise ValueError(f"Invalid item {item_id}")
    await anyio.sleep(1)
    return {"id": item_id}

@router.post("/batch-process")
async def batch_process(item_ids: list[int]):
    results = []
    try:
        async with anyio.create_task_group() as tg:
            for item_id in item_ids:
                # BAD: Bare exception catch inside task group wrapper swallows
                # cancellations and masks sibling exceptions inside the ExceptionGroup.
                tg.start_soon(worker_fetch, item_id)
    except Exception as exc:
        # Masking multi-failure ExceptionGroup into a single generic error
        # Cancels remaining tasks silently without audit or unrolling
        raise HTTPException(status_code=500, detail=str(exc))
```

#### Production Standard (Correct)
> **Standard**: Uses Python 3.11 `except*` syntax to cleanly separate cancellation signals, expected domain validation failures (`ValueError`), and unhandled runtime panics. Ensures that cancellation is respected while cleanup actions are shielded with `anyio.CancelScope(shield=True)`, avoiding zombie states and unhandled coroutine cancellations.
```python
import structlog
import anyio
from fastapi import APIRouter, HTTPException, status

logger = structlog.get_logger(__name__)
router = APIRouter()

async def worker_fetch(item_id: int, results: list[dict]):
    if item_id == 2:
        raise ValueError(f"Invalid item payload for id={item_id}")
    await anyio.sleep(0.1)
    results.append({"id": item_id, "status": "processed"})

@router.post("/batch-process", status_code=status.HTTP_200_OK)
async def batch_process(item_ids: list[int]):
    results: list[dict] = []
    cancelled_cls = anyio.get_cancelled_exc_class()

    try:
        async with anyio.create_task_group() as tg:
            for item_id in item_ids:
                tg.start_soon(worker_fetch, item_id, results)
    except* cancelled_cls:
        # Guarantee teardown logic executes under an uncancelable shield
        with anyio.CancelScope(shield=True):
            await logger.awarning("batch_process.cancelled", processed_count=len(results))
        raise
    except* ValueError as eg:
        # Deterministically unroll domain errors from the ExceptionGroup
        errors = [str(err) for err in eg.exceptions]
        await logger.aerror("batch_process.domain_validation_failure", errors=errors)
        raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail={"errors": errors})
    except* Exception as eg:
        # Unroll remaining unexpected runtime exceptions
        await logger.acritical("batch_process.fatal_worker_error", exception_count=len(eg.exceptions))
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal worker failure")

    return {"processed": results}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: FastAPI AnyIO Structured TaskGroup Exception Propagation and Cancellation Standard

FastAPI AnyIO Structured TaskGroup Exception Propagation and Cancellation Standard defines an industrial-grade behavioral standard for backend engineering. Enforces deterministic handling and explicit unrolling of multi-child ExceptionGroups across request-scoped AnyIO TaskGroups in FastAPI services. Prevents silently swallowed coroutine cancellations, hidden subtask panics, and zombie background worker states through strict isolation and shield invariants. 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: Swallowing BaseExceptions or Blindly Catching Top-Level Exception Around AnyIO TaskGroups 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 DriftSwallowing BaseExceptions or Blindly Catching Top-Level Exception Around AnyIO TaskGroupsCritical
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 handle multi-child exceptions raised by `anyio.create_task_group()` using Python 3.11+ `except*` syntax or explicit recursive inspection via `ExceptionGroup.subgroup()` / `split()`.

Engineers MUST ALWAYS handle multi-child exceptions raised by `anyio.create_task_group()` using Python 3.11+ `except*` syntax or explicit recursive inspection via `ExceptionGroup.subgroup()` / `split()`.

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 use a blanket `except Exception

Engineers MUST NOT use a blanket `except Exception:` block around an `anyio.create_task_group()` context without re-raising or fully dispatching all sub-exceptions contained in the `ExceptionGroup`.

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 NEVER catch, suppress, or mask `anyio.get_cancelled_exc_class()` (or `asyncio.CancelledError`) unless explicitly implementing idempotent cancellation cleanup followed by immediate re-raising.

Engineers MUST NEVER catch, suppress, or mask `anyio.get_cancelled_exc_class()` (or `asyncio.CancelledError`) unless explicitly implementing idempotent cancellation cleanup followed by immediate re-raising.

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

Worker coroutines scheduled via `tg.start_soon()` MUST implement granular local error boundary handling to distinguish deterministic domain failures from unhandled runtime defects before bubbling to the TaskGroup boundary.

Worker coroutines scheduled via `tg.start_soon()` MUST implement granular local error boundary handling to distinguish deterministic domain failures from unhandled runtime defects before bubbling to the TaskGroup boundary.

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

Teardown and persistent resource commits executed during TaskGroup cancellation MUST be wrapped inside `with anyio.CancelScope(shield=True)

Teardown and persistent resource commits executed during TaskGroup cancellation MUST be wrapped inside `with anyio.CancelScope(shield=True):` to prevent premature abort of critical cleanup coroutines.

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

FastAPI request-scoped background worker pools MUST map unhandled child exceptions from `ExceptionGroup` into structured telemetry events before letting the TaskGroup cancel remaining concurrent siblings.

FastAPI request-scoped background worker pools MUST map unhandled child exceptions from `ExceptionGroup` into structured telemetry events before letting the TaskGroup cancel remaining concurrent siblings.

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 Propagation and Cancellation Standard critical for AI coding assistants?

Enforces deterministic handling and explicit unrolling of multi-child ExceptionGroups across request-scoped AnyIO TaskGroups in FastAPI services. Prevents silently swallowed coroutine cancellations, hidden subtask panics, and zombie background worker states through strict isolation and shield invariants. 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 (Swallowing BaseExceptions or Blindly Catching Top-Level Exception Around AnyIO TaskGroups) 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