Logo Blog

Debug compiler errors in Axum handlers

I was adding tokio-postgres to a project that previously used Sqlite when I got the following error:

error[E0277]: the trait bound `fn(State<Arc<AppState>>, Json<CreateShortUrl>) -> impl Future<Output = impl IntoResponse> {create_short_url}: Handler<_, _>` is not satisfied
   --> src/main.rs:39:42
    |
39  |         .route("/create-short-url", post(create_short_url))
    |                                     ---- ^^^^^^^^^^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(State<Arc<AppState>>, Json<...>) -> ... {create_short_url}`
    |                                     |
    |                                     required by a bound introduced by this call
    |
    = help: the following other types implement trait `Handler<T, S>`:
              `Layered<L, H, T, S>` implements `Handler<T, S>`
              `MethodRouter<S>` implements `Handler<(), S>`
note: required by a bound in `post`
   --> /Users/markuswendorf/.cargo/registry/src/index.crates.io-6f17d22bba15001f/axum-0.7.9/src/routing/method_routing.rs:443:1
    |
443 | top_level_handler_fn!(post, POST);
    | ^^^^^^^^^^^^^^^^^^^^^^----^^^^^^^
    | |                     |
    | |                     required by a bound in this function
    | required by this bound in `post`
    = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)

I didn’t really understand what was wrong with my code: this error originates in the macro 'top_level_handler_fn'.

To get better errors from handler implementations you can use the #[axum::debug_handler] attribute. Make sure the macros feature is enabled for Axum:

axum = { version = "0.7.9", features = ["macros"] }

Adding the attribute to my handler reveals the underlying issue:

#[axum::debug_handler]
async fn create_short_url(
    State(state): State<Arc<AppState>>,
    Json(payload): Json<CreateShortUrl>,
) -> impl IntoResponse

The error now reads:

future cannot be sent between threads safely
within `impl Future<Output = impl IntoResponse>`, the trait
`Send` is not implemented for `std::sync::MutexGuard<'_, PostgresStorage>`

Which prompted me to replace the std::sync::Mutex with tokio::sync::Mutex. And voilà the issue is gone.

The tokio::sync::Mutex works here because the tokio::sync::MutexGuard can be held across any .await point as it is Send.