tokio-rs/axum · error · compile_error

missing `#[from_request(via(...))]`

Error message

missing `#[from_request(via(...))]`

What it means

Thrown by `#[derive(FromRequest)]` / `#[derive(FromRequestParts)]` when applied to an `enum` that is missing the required `#[from_request(via(...))]` container attribute. For enums the macro cannot extract field-by-field (there is no fixed set of fields), so it _requires_ a `via(SomeWrapper)` to delegate extraction through a wrapper type (see the `(None, _)` arm at from_request/mod.rs:220). The same code path also fires when `rejection(...)` is used without `via(...)` (the adjacent arm at line 216 gives a different message). Reproducible with `tests/from_request/fail/enum_no_via.rs`.

Source

Thrown at axum-macros/src/from_request/mod.rs:220

                    state_from_via(&ident, via).map(State::Custom)
                })()
                .unwrap_or_else(|| State::Default(syn::parse_quote!(S))),
            };

            match (via.map(second), rejection) {
                (Some(via), rejection) => impl_enum_by_extracting_all_at_once(
                    &ident,
                    variants,
                    &via,
                    rejection.map(second).as_ref(),
                    &state,
                    tr,
                ),
                (None, Some((rejection_kw, _))) => Err(syn::Error::new_spanned(
                    rejection_kw,
                    "cannot use `rejection` without `via`",
                )),
                (None, _) => Err(syn::Error::new(
                    Span::call_site(),
                    "missing `#[from_request(via(...))]`",
                )),
            }
        }
        _ => Err(syn::Error::new_spanned(item, "expected `struct` or `enum`")),
    }
}

fn parse_single_generic_type_on_struct(
    generics: syn::Generics,
    fields: &syn::Fields,
    tr: Trait,
) -> syn::Result<Option<Ident>> {
    if let Some(where_clause) = generics.where_clause {
        return Err(syn::Error::new_spanned(
            where_clause,
            format_args!("#[derive({tr})] doesn't support structs with `where` clauses"),

View on GitHub (pinned to c9a911b799)

Solutions

  1. Add a `via(...)` attribute naming a wrapper whose generic impl already implements FromRequest/FromRequestParts, e.g. `#[from_request(via(Json))]` if the enum is the body type.
  2. If the enum was meant to be extracted from JSON, switch to `#[derive(serde::Deserialize)]` and accept it as `Json<YourEnum>` in the handler instead of deriving `FromRequest`.
  3. Convert the enum into a struct so the macro can extract each field individually via the default per-field code path (no `via` required for structs).
  4. If you genuinely need a custom enum extractor, implement `FromRequest`/`FromRequestParts` by hand instead of using the derive.

Example fix

// before
#[derive(FromRequest, Clone)]
enum Extractor {}

// after
#[derive(FromRequest, Clone)]
#[from_request(via(Json))]
enum Extractor {
    A(String),
    B(i32),
}
Defensive patterns

Strategy: type-guard

Validate before calling

// There's no runtime check — the macro only accepts enums with `via(...)`.
// Establish a project convention: enums that are request bodies MUST be
// wrapped at the call site, e.g. Json<MyEnum>, and never derive FromRequest
// directly. Add a clippy-style lint comment:
//
// // lint: enums require #[from_request(via(...))] when deriving FromRequest
// #[derive(FromRequest)]
// #[from_request(via(Json))]
// enum Body { ... }
//
// CI grep to enforce it:
//   rg --type rust '#\[derive\([^)]*FromRequest' --files-with-matches | \
//     xargs rg -L 'from_request\(via' && echo 'enum missing via' || true

Type guard

// Type-level marker that documents which enums are safe to derive FromRequest on.
// Only implement it for enums that carry #[from_request(via(...))].
pub trait DeriveFromRequestSafe {}

// Example: only the Json-backed enum is marked safe.
impl DeriveFromRequestSafe for MyJsonEnum {}

// Generic helper that rejects anything else at compile time:
pub fn accept<E: DeriveFromRequestSafe>(e: E) { let _ = e; }

Prevention

When it happens

Trigger: Annotated an enum (not a struct) with `#[derive(FromRequest)]` or `#[derive(FromRequestParts)]` and supplied neither `via(...)` nor a valid `rejection(...)` paired with `via(...)`. Specifically the match at line 207 hits the `(None, _)` arm because `via.map(second)` is `None`. Also reachable by writing `#[from_request(rejection = Foo)]` on an enum without `via` — but that hits line 216 and reports "cannot use `rejection` without `via`" instead.

Common situations: Treating an enum like a struct and assuming the derive will generate field extraction. Using an enum to model several extractor shapes (e.g. an API-error enum or a sum-type extractor) without supplying a wrapper that already implements `FromRequest`. Copy-pasting a struct's `#[derive(FromRequest)]` onto an enum during a refactor. Confusing `FromRequest` with serde's `Deserialize`, where enums work out of the box.

Related errors


AI-assisted analysis of tokio-rs/axum@c9a911b799 (2026-08-06). Data as JSON: /data/errors/7381af064cbb1253.json. Report an issue: GitHub.