tokio-rs/axum · error · syn::Error

can't infer state type, please add #[{attr_name}(state = MyS

Error message

can't infer state type, please add #[{attr_name}(state = MyStateType)] attribute

What it means

Thrown by #[derive(FromRequest)] / #[derive(FromRequestParts)] on a struct (from_request/mod.rs:154). The macro infers the state type by scanning fields: infer_state_type_from_field_types collects inner types of any State<T> field, and infer_state_type_from_field_attributes collects the field type of any #[from_request(via(State))] field. If these yield two or more distinct candidate types, state becomes State::CannotInfer. The macro still emits the generated impl (with an unimplemented!() body so downstream errors are visible) followed by this compile_error.

Source

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

                    fields,
                    &via,
                    rejection.as_ref(),
                    generic_ident.as_ref(),
                    &state,
                    tr,
                )?,
                (None, rejection) => {
                    error_on_generic_ident(generic_ident, tr)?;
                    impl_struct_by_extracting_each_field(&ident, &fields, rejection, &state, tr)?
                }
            };

            if matches!(state, State::CannotInfer) {
                let attr_name = match tr {
                    Trait::FromRequest => "from_request",
                    Trait::FromRequestParts => "from_request_parts",
                };
                let compile_error = syn::Error::new(
                    Span::call_site(),
                    format_args!(
                        "can't infer state type, please add \
                         `#[{attr_name}(state = MyStateType)]` attribute",
                    ),
                )
                .into_compile_error();

                Ok(quote! {
                    #trait_impl
                    #compile_error
                })
            } else {
                Ok(trait_impl)
            }
        }
        syn::Item::Enum(item) => {
            let syn::ItemEnum {

View on GitHub (pinned to 151cd5c123)

Solutions

  1. Add the container attribute naming the router's root state: #[from_request(state = AppState)] (or #[from_request_parts(state = AppState)]).
  2. Make all State<T> / #[from_request(via(State))] fields agree on the same inner type.
  3. Drop the redundant State<_> field and obtain sub-state via FromRef instead.

Example fix

// before
#[derive(FromRequest)]
struct Extractor {
    inner_state: State<AppState>,
    other_state: State<OtherState>,
}

// after
#[derive(FromRequest)]
#[from_request(state = AppState)]
struct Extractor {
    inner_state: State<AppState>,
    other_state: State<OtherState>,
}
Defensive patterns

Strategy: validation

Validate before calling

// Safe invocation pattern: if a FromRequest struct has fields whose inferred
// state types could disagree, always set the container `state = ...`.
#[derive(FromRequest)]
#[from_request(state = AppState)] // names the router root state explicitly
struct Extractor {
    inner_state: State<AppState>,
    other_state: State<OtherState>,
}

// Single-state structs infer fine without the attribute:
#[derive(FromRequest)]
struct Simple { s: State<AppState> }

Prevention

When it happens

Trigger: #[derive(FromRequest)] struct Extractor { a: State<AppState>, b: State<OtherState> } with no #[from_request(state = ...)] container attribute, where AppState and OtherState are different types. Also triggered when a State<A> field and a #[from_request(via(State))] field: B disagree (A != B), or when via(State) on the container combined with conflicting field-level State<...> types yields more than one candidate.

Common situations: Building a composite extractor that aggregates several sub-extractors each carrying their own state. Mixing a State<T> field with a #[from_request(via(State))] field on the same struct. Migrating a struct from one state type to another and leaving a stale field behind.

Related errors


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