#2492·axum

`RequestBodyLimitLayer` when applied via `ServiceBuilder` won't compile in v0.7

Author: markgomezCreated Jan 6, 2024Updated Sep 14, 2026
  • [×] I have looked for existing issues (including closed) about this

Bug Report

Version

  • axum v0.7.3
  • axum-core v0.4.2

Platform

  • Darwin Kernel Version 23.0.0: Fri Sep 15 14:41:34 PDT 2023; root:xnu-10002.1.13~1/RELEASE_ARM64_T8103 arm64

Crates

toml
axum = "0.7.3"
tokio = { version = "1.35.1", features = ["full"] }
tower = "0.4.13"
tower-http = { version = "0.5.0", features = ["limit"] }

Description

With ServiceBuilder I was applying multiple middleware, the first of which was RequestBodyLimitLayer and everything compiled in v0.6 (when the B type parameter was still available). With Axum now having its own body type in v0.7, the following example no longer compiles:

rust
use axum::{
    extract::{Request, State},
    middleware::{self, Next},
    response::{Response, Result},
    routing::get,
    Router,
};
use tower::ServiceBuilder;
use tower_http::limit::RequestBodyLimitLayer;

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(|| async { "Hello, Axum!" }))
        .layer(
            ServiceBuilder::new()
                .layer(RequestBodyLimitLayer::new(1_048_576))
                .layer(middleware::from_fn_with_state("state", foo)),
        );

    let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();

    axum::serve(listener, app).await.unwrap();
}

async fn foo(State(_state): State<&'static str>, req: Request, next: Next) -> Result<Response> {
    Ok(next.run(req).await)
}
bash
error[E0277]:
the trait bound `axum::middleware::FromFn<fn(axum::extract::State<&'static str>,
axum::http::Request<axum::body::Body>, axum::middleware::Next) ->
impl std::future::Future<Output = std::result::Result<axum::http::Response<axum::body::Body>,
axum::response::ErrorResponse>> {foo}, &str, axum::routing::Route, _>:
tower::Service<axum::http::Request<tower_http::body::Limited<axum::body::Body>>>` is not satisfied

I noticed in the "Fewer generics" section from the v0.7 announcement blog post that ServiceBuilder wasn't being used and sure enough things will compile if RequestBodyLimitLayer is moved to its own layer (like in the blog post):

diff
 // compiles
 let app = Router::new()
     .route("/", get(|| async { "Hello, Axum!" }))
     .layer(
         ServiceBuilder::new()
-            .layer(RequestBodyLimitLayer::new(1_048_576))
             .layer(middleware::from_fn_with_state("state", foo)),
     )
+    .layer(RequestBodyLimitLayer::new(1_048_576));

To keep RequestBodyLimitLayer a part of ServiceBuilder, things will also compile if it is moved out of the first position (but I will eventually need it to be first):

diff
 // also compiles, but limits need to be applied before everything else, so ultimately won't work
 let app = Router::new()
     .route("/", get(|| async { "Hello, Axum!" }))
     .layer(
         ServiceBuilder::new()
-            .layer(RequestBodyLimitLayer::new(1_048_576))
             .layer(middleware::from_fn_with_state("state", foo))
+            .layer(RequestBodyLimitLayer::new(1_048_576)),
     );

In #1110 (and mentioned in the blog post) the solution back in v0.6 was to extract Request<http_body::Limited<Body>> instead of Request<Body> in subsequent middleware. But with the body type parameter being removed from v0.7, I'm not sure how to satisfy the trait bound in this situation (other than avoid using RequestBodyLimitLayer with ServiceBuilder).