Reputation: 60497
Every one of these handler functions below fail with essentially the exact same error "Handler is not satisfied":
use axum::{extract::Request, http::HeaderMap, routing::get, Json, Router};
fn routes() -> Router {
Router::new()
.route("/a", get(foo_a))
.route("/b", get(foo_b))
.route("/c", get(foo_c))
.route("/d", get(foo_d))
.route("/e", get(foo_e))
.route("/f", get(foo_f))
}
struct MyExtractor;
struct MyResponse;
struct MyBody;
async fn foo_a(_: MyExtractor) -> &'static str {
"hello world!"
}
async fn foo_b() -> MyResponse {
MyResponse
}
async fn foo_c(body: Json<MyBody>) -> Json<MyBody> {
body
}
async fn foo_d(_req: Request, _body: String) -> &'static str {
"yum food"
}
async fn foo_e() -> &'static str {
static LIST: std::sync::Mutex<Vec<i32>> = std::sync::Mutex::new(Vec::new());
let _list = LIST.lock().unwrap();
let _ = foo_b().await;
drop(_list);
"checked it twice"
}
async fn foo_f(_body: String, _headers: HeaderMap) -> &'static str {
"foofy"
}
error[E0277]: the trait bound `fn(MyExtractor) -> impl Future<Output = &'static str> {foo_a}: Handler<_, _>` is not satisfied
--> src/main.rs:5:26
|
5 | .route("/a", get(foo_a))
| --- ^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(MyExtractor) -> impl Future<Output = &'static str> {foo_a}`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `Handler<T, S>`:
<Layered<L, H, T, S> as Handler<T, S>>
<MethodRouter<S> as Handler<(), S>>
note: required by a bound in `axum::routing::get`
--> /home/kmdreko/.cargo/registry/src/index.crates.io-6f17d22bba15001f/axum-0.7.5/src/routing/method_routing.rs:385:1
|
385 | 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)
error[E0277]: the trait bound `fn() -> impl Future<Output = MyResponse> {foo_b}: Handler<_, _>` is not satisfied
--> src/main.rs:6:26
|
6 | .route("/b", get(foo_b))
| --- ^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn() -> impl Future<Output = MyResponse> {foo_b}`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `Handler<T, S>`:
<Layered<L, H, T, S> as Handler<T, S>>
<MethodRouter<S> as Handler<(), S>>
note: required by a bound in `axum::routing::get`
--> /home/kmdreko/.cargo/registry/src/index.crates.io-6f17d22bba15001f/axum-0.7.5/src/routing/method_routing.rs:385:1
|
385 | 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)
error[E0277]: the trait bound `fn(Json<MyBody>) -> impl Future<Output = Json<MyBody>> {foo_c}: Handler<_, _>` is not satisfied
--> src/main.rs:7:26
|
7 | .route("/c", get(foo_c))
| --- ^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Json<MyBody>) -> impl Future<Output = Json<MyBody>> {foo_c}`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `Handler<T, S>`:
<Layered<L, H, T, S> as Handler<T, S>>
<MethodRouter<S> as Handler<(), S>>
note: required by a bound in `axum::routing::get`
--> /home/kmdreko/.cargo/registry/src/index.crates.io-6f17d22bba15001f/axum-0.7.5/src/routing/method_routing.rs:385:1
|
385 | 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)
error[E0277]: the trait bound `fn(axum::http::Request<Body>, String) -> impl Future<Output = &'static str> {foo_d}: Handler<_, _>` is not satisfied
--> src/main.rs:8:26
|
8 | .route("/d", get(foo_d))
| --- ^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(axum::http::Request<Body>, String) -> impl Future<Output = &'static str> {foo_d}`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `Handler<T, S>`:
<Layered<L, H, T, S> as Handler<T, S>>
<MethodRouter<S> as Handler<(), S>>
note: required by a bound in `axum::routing::get`
--> /home/kmdreko/.cargo/registry/src/index.crates.io-6f17d22bba15001f/axum-0.7.5/src/routing/method_routing.rs:385:1
|
385 | 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)
error[E0277]: the trait bound `fn() -> impl Future<Output = &'static str> {foo_e}: Handler<_, _>` is not satisfied
--> src/main.rs:9:26
|
9 | .route("/e", get(foo_e))
| --- ^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn() -> impl Future<Output = &'static str> {foo_e}`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `Handler<T, S>`:
<Layered<L, H, T, S> as Handler<T, S>>
<MethodRouter<S> as Handler<(), S>>
note: required by a bound in `axum::routing::get`
--> /home/kmdreko/.cargo/registry/src/index.crates.io-6f17d22bba15001f/axum-0.7.5/src/routing/method_routing.rs:385:1
|
385 | 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)
error[E0277]: the trait bound `fn(String, HeaderMap) -> impl Future<Output = &'static str> {foo_f}: Handler<_, _>` is not satisfied
--> src/main.rs:10:26
|
10 | .route("/f", get(foo_f))
| --- ^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(String, HeaderMap) -> impl Future<Output = &'static str> {foo_f}`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `Handler<T, S>`:
<Layered<L, H, T, S> as Handler<T, S>>
<MethodRouter<S> as Handler<(), S>>
note: required by a bound in `axum::routing::get`
--> /home/kmdreko/.cargo/registry/src/index.crates.io-6f17d22bba15001f/axum-0.7.5/src/routing/method_routing.rs:385:1
|
385 | 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)
What are the rules for implementing handler functions? How can I find out why they are failing? Why aren't these errors more helpful?
This is meant as a canonical target for common axum handler errors.
Upvotes: 3
Views: 1185
Reputation: 60497
TL;DR: The rules are below but #[debug_handler]
can get you on your way quickly.
The best resources are spread between the axum::handler
and axum::extract
modules. The former has a section that enumerates handlers:
- Are
async
fns.- Take no more than 16 arguments that all implement
Send
.- All except the last argument implement
FromRequestParts
.- The last argument implements
FromRequest
.- Returns something that implements
IntoResponse
.- If a closure is used it must implement
Clone + Send
and be'static
.- Returns a future that is
Send
. The most common way to accidentally make a future!Send
is to hold a!Send
type across an await.
Examples are always good so lets look at each function to see why they don't work:
async fn foo_a(_: MyExtractor) -> &'static str {
"hello world!"
}
This does not work since there is not a FromRequestParts
or FromRequest
implementation for MyExtractor
.
async fn foo_b() -> MyResponse {
MyResponse
}
This does not work since the return type does not implement IntoResponse
.
async fn foo_c(body: Json<MyBody>) -> Json<MyBody> {
body
}
This one is trickier because axum's Json
type does implement both FromRequest
and IntoResponse
, but it still fails because additional constraints were not met. This can affect a number of extractor types:
T
must implement DeserializeOwned
for Json<T>
to implement FromRequest
T
must implement Serialize
for Json<T>
to implement IntoResponse
T
must be Clone
for Extension<T>
to implement FromRequestParts
T
must implement DeserializeOwned
for Path<T>
to implement FromRequestParts
T
must implement DeserializeOwned
for Query<T>
to implement FromRequestParts
async fn foo_d(_req: Request, _body: String) -> &'static str {
"yum food"
}
This one does not work because both Request
and String
implement FromRequest
when only one of them is allowed. Otherwise both will attempt to consume the request body.
async fn foo_e() -> &'static str {
static LIST: std::sync::Mutex<Vec<i32>> = std::sync::Mutex::new(Vec::new());
let _list = LIST.lock().unwrap();
let _ = foo_b().await;
drop(_list);
"checked it twice"
}
This one does not compile since the Future
being returned is !Send
. This can happen when a !Send
type is used across an .await
point.
Mutex
guard from the standard library does not implement Send
. Use parking-lot or an async mutex.Rc
does not implement Send
and can be a common culprit.async fn foo_f(_body: String, _headers: HeaderMap) -> &'static str {
"foofy"
}
This one only has one extractor that consumes the body, but it must go at the end. Here the HeaderMap
extractor needs to be listed first.
not mentioned but there's a whole concept of State
that can propagate through routers and into handlers where a mismatch can cause the same error.
However, even if you know these rules, it may not be clear which one is in error. An extractor may have yet another constraint that you don't know is missing. But the error message doesn't give you any indication which part is wrong, so you could end up wasting type digging into a response problem, when ultimately you accidentally introduced a !Send
type or something.
Is there a better option? Yes, axum provides a #[debug_handler]
macro that you can annotate your failing handler functions with which will make it generate better errors. Though be sure to enable the "macros"
feature:
axum = { version = "...", features = ["macros"] }
#[axum::debug_handler]
async fn foo_a(_: MyExtractor) -> &'static str {
"hello world!"
}
Here is what the errors above look like when all handlers are annotated with #[debug_handler]
:
error: Can't have two extractors that consume the request body. `Request<_>` and `String` both do that.
--> src/main.rs:33:16
|
33 | async fn foo_d(_req: Request, _body: String) -> &'static str {
| ^^^^
error: `String` consumes the request body and thus must be the last argument to the handler function
--> src/main.rs:49:23
|
49 | async fn foo_f(_body: String, _headers: HeaderMap) -> &'static str {
| ^^^^^^
error[E0277]: the trait bound `for<'de> MyBody: serde::de::Deserialize<'de>` is not satisfied
--> src/main.rs:28:22
|
28 | async fn foo_c(body: Json<MyBody>) -> Json<MyBody> {
| ^^^^ the trait `for<'de> serde::de::Deserialize<'de>` is not implemented for `MyBody`
|
= help: the following other types implement trait `serde::de::Deserialize<'de>`:
bool
char
isize
i8
i16
i32
i64
i128
and 144 others
= note: required for `MyBody` to implement `serde::de::DeserializeOwned`
= note: required for `Json<MyBody>` to implement `FromRequest<()>`
= help: see issue #48214
error[E0277]: the trait bound `MyResponse: IntoResponse` is not satisfied
--> src/main.rs:23:21
|
23 | async fn foo_b() -> MyResponse {
| ^^^^^^^^^^ the trait `IntoResponse` is not implemented for `MyResponse`
|
= help: the following other types implement trait `IntoResponse`:
Box<str>
Box<[u8]>
axum::body::Bytes
Body
axum::extract::rejection::FailedToBufferBody
axum::extract::rejection::LengthLimitError
axum::extract::rejection::UnknownBodyError
axum::extract::rejection::InvalidUtf8
and 121 others
note: required by a bound in `__axum_macros_check_foo_b_into_response::{closure#0}::check`
--> src/main.rs:23:21
|
23 | async fn foo_b() -> MyResponse {
| ^^^^^^^^^^ required by this bound in `check`
error[E0277]: the trait bound `MyExtractor: FromRequestParts<()>` is not satisfied
--> src/main.rs:18:19
|
18 | async fn foo_a(_: MyExtractor) -> &'static str {
| ^^^^^^^^^^^ the trait `FromRequestParts<()>` is not implemented for `MyExtractor`
|
= help: the following other types implement trait `FromRequestParts<S>`:
<HeaderMap as FromRequestParts<S>>
<Extension<T> as FromRequestParts<S>>
<Method as FromRequestParts<S>>
<axum::http::request::Parts as FromRequestParts<S>>
<Uri as FromRequestParts<S>>
<Version as FromRequestParts<S>>
<ConnectInfo<T> as FromRequestParts<S>>
<Extensions as FromRequestParts<S>>
and 28 others
= note: required for `MyExtractor` to implement `FromRequest<(), axum_core::extract::private::ViaParts>`
note: required by a bound in `__axum_macros_check_foo_a_0_from_request_check`
--> src/main.rs:18:19
|
18 | async fn foo_a(_: MyExtractor) -> &'static str {
| ^^^^^^^^^^^ required by this bound in `__axum_macros_check_foo_a_0_from_request_check`
error: future cannot be sent between threads safely
--> src/main.rs:37:1
|
37 | #[axum::debug_handler]
| ^^^^^^^^^^^^^^^^^^^^^^ future returned by `foo_e` is not `Send`
|
= help: within `impl Future<Output = &'static str>`, the trait `Send` is not implemented for `MutexGuard<'_, Vec<i32>>`
note: future is not `Send` as this value is used across an await
--> src/main.rs:42:21
|
41 | let _list = LIST.lock().unwrap();
| ----- has type `MutexGuard<'_, Vec<i32>>` which is not `Send`
42 | let _ = foo_b().await;
| ^^^^^ await occurs here, with `_list` maybe used later
note: required by a bound in `__axum_macros_check_foo_e_future::check`
--> src/main.rs:37:1
|
37 | #[axum::debug_handler]
| ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check`
= note: this error originates in the attribute macro `axum::debug_handler` (in Nightly builds, run with -Z macro-backtrace for more info)
It may take multiple passes of fixing, and the original error may still be listed, but these error messages produced by the attribute give you a much better indication than without.
Why aren't these errors more helpful?
It has to do with the way that functions with differing numbers of parameters can be used to satisfy the same constraint. A naïve implementation would run into conflicts, so it requires some trickery. See the Q&A here for roughly how it is done. But in the end, all that is asked to the compiler is "does this function satisfy Handler
" and the only effective answer by the compiler is "yes" (and it compiles) or "no" (and you get a generic error).
Upvotes: 5