{"record":{"id":"45e96cc3b186a075","repo":"tokio-rs/axum","slug":"can-t-infer-state-type-please-add-set-it-explicit","errorCode":null,"errorMessage":"can't infer state type, please add set it explicitly, as in #[axum_macros::debug_{kind}(state = MyStateType)]","messagePattern":"can't infer state type, please add set it explicitly, as in #\\[axum_macros::debug_(.+?)\\(state = MyStateType\\)\\]","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"axum-macros/src/debug_handler.rs","lineNumber":38,"sourceCode":"        check_output_impls_into_response(item_fn)\n    } else {\n        check_output_tuples\n    };\n\n    // If the function is generic, we can't reliably check its inputs or whether the future it\n    // returns is `Send`. Skip those checks to avoid unhelpful additional compiler errors.\n    let check_inputs_and_future_send = if item_fn.sig.generics.params.is_empty() {\n        let mut err = None;\n\n        if state_ty.is_none() {\n            let state_types_from_args = state_types_from_args(item_fn);\n\n            #[allow(clippy::comparison_chain)]\n            if state_types_from_args.len() == 1 {\n                state_ty = state_types_from_args.into_iter().next();\n            } else if state_types_from_args.len() > 1 {\n                err = Some(\n                    syn::Error::new(\n                        Span::call_site(),\n                        format!(\n                            \"can't infer state type, please add set it explicitly, as in \\\n                            `#[axum_macros::debug_{kind}(state = MyStateType)]`\"\n                        ),\n                    )\n                    .into_compile_error(),\n                );\n            }\n        }\n\n        err.unwrap_or_else(|| {\n            let state_ty = state_ty.unwrap_or_else(|| syn::parse_quote!(()));\n\n            let check_future_send = check_future_send(item_fn, kind);\n\n            if let Some(check_input_order) = check_input_order(item_fn, kind) {\n                quote! {","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/tokio-rs/axum/blob/151cd5c12325373b86daf405a6afc0a0086a6706/axum-macros/src/debug_handler.rs#L20-L56","documentation":"Thrown by the #[debug_handler] / #[debug_middleware] attribute macro (debug_handler.rs:38). To type-check a handler, the macro must know the router's state type. It tries to infer it by scanning the fn arguments for axum::extract::State<T> wrappers (state_types_from_args -> infer_state_types). If exactly one distinct inner type is found it uses it; if two or more different inner types are found it cannot decide which is the root state and emits this compile error, asking you to set it explicitly with state = .... Inference is skipped entirely for generic functions (a different error is emitted there).","triggerScenarios":"A non-generic async fn decorated with #[debug_handler] (or #[debug_middleware]) that takes two or more State<T> arguments whose inner T differs (e.g. State(a): State<AppState> and State(b): State<InnerState>), and the attribute does NOT set state = .... The macro collects the distinct state types into a HashSet, finds len() > 1, and returns the error via err.unwrap_or_else.","commonSituations":"Using nested/sub-state via FromRef where a handler pulls both the root AppState and a sub-state InnerState through two separate State<_> extractors. Refactoring that introduces a second State<_> argument. Copy-pasting a handler from a single-state router into a multi-state one. Forgetting that the macro infers from the argument list, not from Router::with_state.","solutions":["Add the state explicitly to the attribute, pointing at the type passed to Router::with_state: #[debug_handler(state = AppState)].","If one of the State<_> arguments is really a sub-state, remove it and obtain the sub-state via FromRef<AppState> from the single root State<AppState> argument.","If unsure which type is the root state, remove the macro temporarily to read the underlying Handler trait error, then re-add it with the explicit state."],"exampleFix":"// before\n#[debug_handler]\nasync fn handler(\n    State(a): State<AppState>,\n    State(b): State<InnerState>,\n) {}\n\n// after\n#[debug_handler(state = AppState)]\nasync fn handler(\n    State(a): State<AppState>,\n    State(b): State<InnerState>,\n) {}\n\n// (InnerState is obtained via `impl FromRef<AppState> for InnerState`)","handlingStrategy":"validation","validationCode":"// Safe invocation pattern: a handler with multiple State<_> extractors MUST\n// declare the root state explicitly. Use this form whenever len(distinct State inner types) > 1.\n//\n// #[debug_handler(state = <type passed to Router::with_state>)]\n#[debug_handler(state = AppState)]\nasync fn handler(\n    State(a): State<AppState>,\n    State(b): State<InnerState>, // InnerState: impl FromRef<AppState>\n) {}\n\n// One-state handlers infer automatically and need no `state = ...`:\n#[debug_handler]\nasync fn simple(State(s): State<AppState>) {}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Whenever a handler takes more than one State<T>, set #[debug_handler(state = RootState)] explicitly — do not rely on inference.","Prefer a single State<RootState> argument and obtain sub-states through FromRef<RootState> instead of multiple State<_> extractors.","Run debug/test builds often: #[debug_handler] is a no-op under the release profile, so state-inference mistakes only surface in debug.","Keep the type passed to Router::with_state as the single source of truth and reference it verbatim in state = ...."],"tags":["axum","axum-macros","debug-handler","state","proc-macro","compile-error"],"backgroundTag":null,"analyzedSha":"151cd5c12325373b86daf405a6afc0a0086a6706","analyzedAt":"2026-08-11T16:57:37.422Z","contentChangedAt":"2026-08-11T16:57:37.422Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}