Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handlers that don't satisfy OperationHandler return cryptic error messages, making it hard to find out what to do to to satisfy it #189

Open
BLucky-gh opened this issue Feb 13, 2025 · 1 comment

Comments

@BLucky-gh
Copy link

BLucky-gh commented Feb 13, 2025

When trying to add an api route handler that returns an eyre::Result<T> (which is a type alias for Result<T, eyre::Report>), I get the following error message:

error[E0277]: the trait bound `fn() -> impl Future<Output = Result<(), ErrReport>> {eyre_example}: OperationHandler<_, _>` is not satisfied
   --> src/main.rs:12:54
    |
12  |         .api_route("/eyre", aide::axum::routing::get(eyre_example)) //error
    |                             ------------------------ ^^^^^^^^^^^^ the trait `OperationHandler<_, _>` is not implemented for fn item `fn() -> impl Future<Output = Result<(), ErrReport>> {eyre_example}`
    |                             |
    |                             required by a bound introduced by this call
    |
    = help: the trait `OperationHandler<I, O>` is implemented for `Layered<L, H, T, S>`
note: required by a bound in `aide::axum::routing::get`
   --> /Users/vusalshahbaz/.cargo/registry/src/index.crates.io-6f17d22bba15001f/aide-0.14.1/src/axum/routing.rs:345:1
    |
345 | method_router_top_level!(get, get_with);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^---^^^^^^^^^^^
    | |                        |
    | |                        required by a bound in this function
    | required by this bound in `get`
    = note: this error originates in the macro `method_router_top_level` (in Nightly builds, run with -Z macro-backtrace for more info)

I understand I'm on my own when I use eyre, and I'm already dealing with it myself (in my real code I'm actually wrapping it in a newtype that I'm implementing the traits for myself, such as IntoResponse, but the error messages are really cryptic, mostly because everything is hidden in a macro, but it's made worse by OperationHandler being a private type which is also hidden in docs, so I don't even know what it wants, why it's there, and how to implement it.

I have dug around in the source and found out that OperationHandler has a blanket implementation over anything that returns OperationOutput, and that has a blanket implementation for a lot of std types, including over Result<T,E> where both implement already implement it, and eyre::Report doesn't impl OperationOutput, which is a quick fix since I already have a newtype over it, but while OperationOutput does have docs, at this point, I already expected it not to so that took some more digging around in the sources. I feel like this could be documented better, because most people wouldn't even go dig in source code to find out that the undocumented type has a blanket implementation for everything that implements a documented trait, since the error message doesn't mention the documented trait, and in fact mentions some completely unrelated trait.

I know that what the compiler suggests or prints isn't really in the developer's control (there are ways to coax it, but it's infeasible most of the time), but a mention in the docs or something like that might work better

here's a quick repro:

use aide::{axum::ApiRouter, openapi::OpenApi};
async fn eyre_example() -> eyre::Result<()> {
    Ok(())
}
async fn std_example() -> Result<(), ()> {
    Ok(())
}

#[tokio::main]
async fn main() -> eyre::Result<()> {
    let app = ApiRouter::new()
        .api_route("/eyre", aide::axum::routing::get(eyre_example)) //error
        .api_route("/std", aide::axum::routing::get(std_example)) //works
        .finish_api(&mut OpenApi::default());

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

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

    Ok(())
}

with the following Cargo.toml

[package]
name = "repro"
version = "0.1.0"
edition = "2021"

[dependencies]
aide = { version = "0.14.0", features = ["axum", "scalar"] }
axum = "0.8.1"
eyre = "0.6.12"
tokio = { version = "1.43.0", features = ["full"] }
@tamasfe
Copy link
Owner

tamasfe commented Feb 13, 2025

OperationHandler is hidden because it is basically a hack that allows for variadic functions. It is implemented for functions that are valid axum handlers while all the parameters implement OperationInput and the return value implements OperationOutput.

I'm not against revealing it in docs, we could also add a diagnostics message, like axum has it here. PRs are welcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants