tokio-rs/axum · error · syn::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 (from_request/mod.rs:220). The enum branch of expand requires a #[from_request(via(SomeWrapper))] container attribute, because the generated impl converts Wrapper<Self> into Self for each variant (impl_enum_by_extracting_all_at_once). The match on (via, rejection) handles: Some(via) -> OK; (None, Some(rejection_kw)) -> 'cannot use rejection without via'; (None, _) -> this 'missing #[from_request(via(...))]' error. Field-by-field extraction is only supported on structs, not enums.

Solutions

  1. Add #[from_request(via(YourWrapper))] to the enum, where YourWrapper<Self>: FromRequest (e.g. a newtype wrapper, Json, Query, or a custom wrapper).
  2. If you wanted per-field extraction, switch the item from an enum to a struct (enums only support all-at-once extraction via a wrapper).
  3. If you also need a custom rejection type, supply both: #[from_request(via(Wrapper), rejection(MyRejection))].

Example fix

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

// after (enum requires `via`)
#[derive(FromRequest, Clone)]
#[from_request(via(MyWrapper))]
enum Extractor {}

// or switch to a struct if field-by-field extraction is intended
Defensive patterns

Strategy: validation

Validate before calling

// Safe invocation pattern: enums MUST declare `via(...)`.
#[derive(FromRequest, Clone)]
#[from_request(via(MyWrapper))] // MyWrapper<Self>: FromRequest<S>
enum Extractor {
    A(A),
    B(B),
}

// If you need field-by-field extraction, use a struct instead (enums do not support it):
#[derive(FromRequest)]
struct Extractor { a: A, b: B }

Prevention

When it happens

Trigger: #[derive(FromRequest)] enum Extractor { ... } (or #[derive(FromRequestParts)] enum ...) with no #[from_request(via(...))] container attribute. The match falls through to (None, _) because neither via nor rejection_kw is present.

Common situations: Converting a struct-based extractor to an enum (e.g. a result-like enum) and forgetting that enums mandate via. Writing an enum newtype around another extractor without specifying the wrapper. Adding #[derive(FromRequest)] to an enum that previously only derived Clone.

Related errors


AI-assisted analysis of tokio-rs/axum@151cd5c123 (2026-08-11). Data as JSON: /api/errors/5c96f1fadeba3c2c. Report an issue: GitHub.

Appendix: 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 151cd5c123)