This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
backendcritical PriorityID: axum-08-cancellation-safe-tower-streaming-boundary

Axum 0.8 Cancellation-Safe Tower Middleware Streaming Boundary

Enforces strict cancellation safety across Axum 0.8 response streaming pipelines and Tower middleware layers. Prevents dangling database connection leases, lock leaks, and half-consumed stream buffer deadlocks when HTTP/2 RST_STREAM frames or HTTP/3 stream aborts occur mid-flight.

Prohibited Anti-Pattern: Unbounded Body Buffering and RAII Drop Bypass in Asynchronous Stream Pipelines

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Axum 0.8 Cancellation-Safe Tower Middleware Streaming Boundary - Enforces strict cancellation safety across Axum 0.8 response streaming pipelines and Tower middleware layers. Prevents dangling database connection leases, lock leaks, and half-consumed stream buffer deadlocks when HTTP/2 RST_STREAM frames or HTTP/3 stream aborts occur mid-flight.
globs: src/**/*.rs
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/axum-08-cancellation-safe-tower-streaming-boundary"
---

# Axum 0.8 Cancellation-Safe Tower Middleware Streaming Boundary

> **Severity**: CRITICAL | **Category**: backend
> **Anti-Pattern Warning**: Unbounded Body Buffering and RAII Drop Bypass in Asynchronous Stream Pipelines

## System Constraints
You are a Principal Rust Architect enforcing Axum 0.8 cancellation safety and resource lifecycle invariants. Mandate that all streaming response bodies, Tower layers, and upstream I/O pipelines (database queries, object storage, channel bridges) cleanly release pooled resources, abort background worker loops via RAII Drop guards, and guarantee zero-deadlock semantics upon sudden client transport termination.

## Strict Guidelines
- Engineers MUST ALWAYS wrap stream producers in RAII drop guards or use `tokio_util::sync::CancellationToken` so that dropping the response body actively terminates upstream queries and releases connection pool handles.
- Engineers MUST NOT spawn detached `tokio::spawn` worker tasks to feed `mpsc::channel` streams without tracking task handles via an abort-on-drop guard (e.g., `tokio_util::task::AbortOnDropHandle`).
- Tower middleware wrapping streaming endpoints MUST NOT hold acquired database transactions (`sqlx::Transaction`), distributed locks, or semaphore permits across `call()` future execution unless explicitly bound to the downstream `Body` stream lifecycle.
- Streams bridging into `axum::body::Body` SHOULD use `SyncWrapper` or `tokio_stream::wrappers::ReceiverStream` ensuring that downstream `poll_next` cancellation immediately causes channel sender disconnect and triggers `Drop` on pending chunks.
- Handlers MUST NEVER collect an infinite or client-driven stream into an unmetered intermediate buffer (e.g., `axum::body::to_bytes` without `LengthLimit`) before propagating errors or client disconnects.

## Bad vs Good Implementations
### Cancellation-Safe Response Streaming with RAII Drop Abort Guard

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Spawning an un-guarded background task decouples the stream generation from the Axum HTTP response lifecycle. When an HTTP/2 client sends an RST_STREAM, Axum drops the response body, but the background task continues until the next failed send attempt, potentially leaving database locks or expensive queries active and leaking connection pool capacity.
```rust
use axum::{response::Response, body::Body, extract::State};
use tokio::sync::mpsc;
use sqlx::PgPool;

pub async fn stream_records(State(pool): State<PgPool>) -> Response {
    let (tx, rx) = mpsc::channel(16);
    // LEAK: Detached task runs indefinitely if HTTP client disconnects
    tokio::spawn(async move {
        let mut conn = pool.acquire().await.unwrap();
        let mut rows = sqlx::query!("SELECT id, data FROM large_table").fetch(&mut *conn);
        while let Some(Ok(row)) = rows.next().await {
            // If client dropped connection, send fails, but lock/resources
            // may remain allocated during pending work or ungraceful loop breaks.
            if tx.send(Ok::<_, std::io::Error>(row.data.into())).await.is_err() {
                break;
            }
        }
    });

    Response::new(Body::from_stream(tokio_stream::wrappers::ReceiverStream::new(rx)))
}
```

#### Production Standard (Correct)
> **Standard**: By inlining the stream production via `async_stream::stream!` directly into `Body::from_stream`, the future's state machine is driven by Axum's downstream poll calls. If the client disconnects, Axum drops the `Body`, which synchronously drops the generator future, automatically executing RAII teardown for the `sqlx` database connection lease.
```rust
use axum::{response::Response, body::Body, extract::State};
use tokio::sync::mpsc;
use tokio_util::task::AbortOnDropHandle;
use async_stream::stream;
use sqlx::PgPool;

pub async fn stream_records(State(pool): State<PgPool>) -> Response {
    // Bound stream directly to the Body lifecycle using async-stream with embedded connection lifecycle.
    // When Axum drops Body on RST_STREAM, the stream future is dropped immediately at its current .await point,
    // cleanly invoking RAII Drop on `conn` and releasing it back to the PgPool.
    let s = stream! {
        let mut conn = match pool.acquire().await {
            Ok(c) => c,
            Err(err) => {
                yield Err(std::io::Error::new(std::io::ErrorKind::Other, err));
                return;
            }
        };
        let mut rows = sqlx::query!("SELECT id, data FROM large_table").fetch(&mut *conn);
        while let Some(result) = rows.next().await {
            match result {
                Ok(row) => yield Ok(row.data.into()),
                Err(err) => {
                    yield Err(std::io::Error::new(std::io::ErrorKind::ConnectionAborted, err));
                    break;
                }
            }
        }
    };

    Response::new(Body::from_stream(s))
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Axum 0.8 Cancellation-Safe Tower Middleware Streaming Boundary

Axum 0.8 Cancellation-Safe Tower Middleware Streaming Boundary defines an industrial-grade behavioral standard for backend engineering. Enforces strict cancellation safety across Axum 0.8 response streaming pipelines and Tower middleware layers. Prevents dangling database connection leases, lock leaks, and half-consumed stream buffer deadlocks when HTTP/2 RST_STREAM frames or HTTP/3 stream aborts occur mid-flight. 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: Unbounded Body Buffering and RAII Drop Bypass in Asynchronous Stream Pipelines 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 DriftUnbounded Body Buffering and RAII Drop Bypass in Asynchronous Stream PipelinesCritical
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 wrap stream producers in RAII drop guards or use `tokio_util

Engineers MUST ALWAYS wrap stream producers in RAII drop guards or use `tokio_util::sync::CancellationToken` so that dropping the response body actively terminates upstream queries and releases connection pool handles.

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 `tokio

Engineers MUST NOT spawn detached `tokio::spawn` worker tasks to feed `mpsc::channel` streams without tracking task handles via an abort-on-drop guard (e.g., `tokio_util::task::AbortOnDropHandle`).

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

Tower middleware wrapping streaming endpoints MUST NOT hold acquired database transactions (`sqlx

Tower middleware wrapping streaming endpoints MUST NOT hold acquired database transactions (`sqlx::Transaction`), distributed locks, or semaphore permits across `call()` future execution unless explicitly bound to the downstream `Body` stream lifecycle.

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

Streams bridging into `axum

Streams bridging into `axum::body::Body` SHOULD use `SyncWrapper` or `tokio_stream::wrappers::ReceiverStream` ensuring that downstream `poll_next` cancellation immediately causes channel sender disconnect and triggers `Drop` on pending chunks.

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

Handlers MUST NEVER collect an infinite or client-driven stream into an unmetered intermediate buffer (e.g., `axum

Handlers MUST NEVER collect an infinite or client-driven stream into an unmetered intermediate buffer (e.g., `axum::body::to_bytes` without `LengthLimit`) before propagating errors or client disconnects.

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 Axum 0.8 Cancellation-Safe Tower Middleware Streaming Boundary critical for AI coding assistants?

Enforces strict cancellation safety across Axum 0.8 response streaming pipelines and Tower middleware layers. Prevents dangling database connection leases, lock leaks, and half-consumed stream buffer deadlocks when HTTP/2 RST_STREAM frames or HTTP/3 stream aborts occur mid-flight. 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 (Unbounded Body Buffering and RAII Drop Bypass in Asynchronous Stream Pipelines) 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

Axum