Axum handler not handling

I've been using Rust for around 3 years now, and I'm running into a problem with an Axum handler. If you could offer some guidance, it would be much appreciated.

use axum::{Router, extract::State, response::IntoResponse, routing::get};
use std::sync::Arc;

use crate::web::WebAppState;

pub fn debug_router(state: Arc<WebAppState>) -> Router<Arc<WebAppState>> {
    Router::new()
        .route("/seed_db", get(seed_db_handler))
        .with_state(state)
}

#[axum::debug_handler]
pub async fn seed_db_handler(State(state): State<Arc<WebAppState>>) -> impl IntoResponse {
    let res = db::seed_database(&state.repo.clone_inner()).await;
    match res {
        Ok(_) => "Database Seeded Successfully!".into_response(),
        Err(e) => format!("Error Seeding Database: {:?}", e).into_response(),
    }
}

When I try to compile this code I get this error:

error[E0277]: the trait bound `fn(axum::extract::State<Arc<WebAppState>>) -> impl futures_util::Future<Output = impl IntoResponse> {web::debug::seed_db_handler}: Handler<_, _>` is not satisfied
   --> web\src\web\debug.rs:8:32
    |
8   |         .route("/seed_db", get(seed_db_handler))
    |                            --- ^^^^^^^^^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(axum::extract::State<Arc<WebAppState>>) -> impl futures_util::Future<Output = impl IntoResponse> {web::debug::seed_db_handler}`
    |                            |
    |                            required by a bound introduced by this call
    |
    = note: Consider using `#[axum::debug_handler]` to improve the error message
    = help: the following other types implement trait `Handler<T, S>`:
              `MethodRouter<S>` implements `Handler<(), S>`
              `axum::handler::Layered<L, H, T, S>` implements `Handler<T, S>`
note: required by a bound in `axum::routing::get`
   --> C:\Users\User\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\axum-0.8.4\src\routing\method_routing.rs:441:1
    |
441 | top_level_handler_fn!(get, GET);
    | ^^^^^^^^^^^^^^^^^^^^^^---^^^^^^
    | |                     |
    | |                     required by a bound in this function
    | required by this bound in `get`
    = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)

I've gone through the Handler trait requirements on docs.rs and I believe I'm using it correctly, but it still is not compiling. Also, the #[debug_handler] macro is does not give any helpful guidance, the compiler output is the same with and without it.

This router is gated behind a #[cfg(debug_assertions)] in my main axum setup, like this:

let router = Router::new().route( ... ).with_state(state);
#[cfg(debug_assertions)]
let router = router.nest("/debug", debug::debug_router(self.clone()));

Removing the #[cfg(debug_assertions)] does not solve the problem.

rust-analyzer also gives an error for one of my other handlers with the same error. The thing is I don't believe I have even changed that handler since the code compiled last. This may just be a problem with r-a.

If you could offer some guidance, it would be much appreciated.

My suspicion would be, that the future returned by seed_db_handler is !Send. What does WebAppState look like? What does db::seed_database do?

1 Like

You can quickly check that a future is Send using this trick:

fn assert_send<T: Send>(f: T) -> T {
    f
}

And then:

let res = assert_send(db::seed_database(&state.repo.clone_inner())).await;

If you get a compile error it's not Send.

2 Likes

That seems to be the case.

fn assert_send<T: Send>(f: T) -> T {
    f
}

async fn test(state: Arc<WebAppState>) {
    let res = assert_send(db::seed_database(&state.repo.clone_inner())).await;
}

This code does not compile because seed_database is !Send. After commenting out function calls to determine which child function of seed_database was causing the compile error, my assumption is that functions that take an impl sqlx::Acquire return a future that is !Send. I was using Acquire because I wanted to seed the whole database, or nothing at all using a transaction.

It seems the problem is that I was using a function that takes an impl Acquire somewhere in a function lower in the call chain of the handler. After removing that, it just works as expected, and no more compile error.

What I don't understand is that my other axum handlers do use database repository methods that take impl Acquire, and eventually all of my database functions do call functions that take impl Acquire, but those are compiling fine for other handlers. I must be misunderstanding something.

For instance

/// Handler for `POST /auth/login`.
/// Checks if the user is authorized and initiates the OAuth flow.
#[axum::debug_handler]
pub async fn login_post(
    State(state): State<Arc<WebAppState>>,
    Form(form): Form<EmailForm>,
) -> impl IntoResponse {
    let res = state.repo.user.get_by_email(&form.email).await;
    // ...
}

This function does not cause a compile error, even though state.repo.user.get_by_email looks like this:

async fn get_by_email(&self, email: &str) -> sqlx::Result<Option<User>> {
    self.get_by_email_tx(&self.db, email).await
}

async fn get_by_email_tx<'c, A>(&self, acquire: A, email: &str) -> sqlx::Result<Option<User>>
where
    A: Acquire<'c, Database = sqlx::Any>,
{
    let mut conn = acquire.acquire().await?;
    sqlx::query_as("SELECT * FROM users WHERE email = $1")
        .bind(email)
        .fetch_optional(&mut *conn)
        .await
}

Have you tried changing the function's signature to take impl Acquire + Send?

Yes. Here is a minimized version of seed_database.

pub async fn seed_database(&self) -> sqlx::Result<()> {
    Ok(self.seed_database_tx(&self.pool).await?)
}

pub async fn seed_database_tx<A>(&self, acquire: A) -> sqlx::Result<()>
where
    A: Acquire<'static, Database = sqlx::Any> + Send,
{
    println!("Seeding database with initial data...");
    let mut conn = acquire.acquire().await?;

    // --- Seed States and Counties ---
    println!("  - Seeding states and counties...");
    let states_and_counties = get_states_and_counties();
    for (state_name, counties) in states_and_counties {
        let state_id = self.state.create_state_tx(&mut *conn, state_name).await?;
        // let state_id = self.state.create_state(state_name).await?;
    }
    Ok(())
}

The above code does not compile when using create_state_tx, but does when using create_state. Here is the definition.

async fn create_state(&self, name: &str) -> sqlx::Result<i64> {
    self.create_state_tx(&self.pool, name).await
}

async fn create_state_tx<'c, A>(&self, acquire: A, name: &str) -> sqlx::Result<i64>
where
    A: Acquire<'c, Database = sqlx::Any> + Send,
{
    let mut conn = acquire.acquire().await?;
    let row = sqlx::query("INSERT INTO states (name) VALUES (?) RETURNING id")
        .bind(name)
        .fetch_one(&mut *conn)
        .await?;
    Ok(row.get(0))
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.