{"id":"dc3183732d304777","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":"compile_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/c9a911b7999de50e9e5023942ca072e9725ae943/axum-macros/src/debug_handler.rs#L20-L56","documentation":"Thrown by the `#[debug_handler]` / `#[debug_middleware]` macros when, after scanning the handler's arguments, the macro finds more than one distinct state type (i.e. more than one `State<T>` with different `T`s) and therefore cannot decide which single type to use as the `S` parameter for the `FromRequestParts<S>` / `FromRequest<S>` checks it generates. Because the macro emits real compile-time trait checks against this inferred state, an ambiguous guess would produce misleading downstream errors, so it bails early and asks the developer to name the state explicitly via `state = MyStateType`.","triggerScenarios":"A non-generic handler annotated with `#[debug_handler]` (or `#[debug_middleware]`) takes two or more `State<...>` arguments whose inner types differ (e.g. `State<AppState>` and `State<OtherState>`) AND the `state = ...` key is not present on the attribute. The branch is reached at debug_handler.rs:36 (`state_types_from_args.len() > 1`) because `infer_state_types` (lib.rs:758) collects every `State<T>` it can see in the fn signature and finds >1 distinct type. Note this path only runs when `state_ty.is_none()` (line 30) and the function has no generics (line 27); generic handlers fail with a different \"doesn't support generic functions\" error.","commonSituations":"Refactoring a handler to depend on substates via `FromRef` (e.g. `State<Database>` + `State<Config>`) without realizing `#[debug_handler]` can only target one router state. Copying a handler from a router with a single shared `AppState` into a router whose state is split into several `FromRef` providers. Adding a second `State<...>` extractor while leaving the existing `#[debug_handler]` (no `state` key) in place. Most often a code-organization change rather than an upgrade/env issue.","solutions":["Add an explicit state type to the attribute, e.g. `#[debug_handler(state = AppState)]`, naming the top-level router state that yields every other sub-state through `FromRef`.","Collapse the multiple `State<T>` arguments into a single `State<AppState>` and read sub-state via `FromRef` inside the handler body, so only one state type is inferable.","If the second `State<T>` is actually a different extractor in disguise, switch it to a non-`State` extractor (e.g. `Extension<T>` or a custom `FromRequestParts`) so it no longer participates in state inference.","Remove `#[debug_handler]` temporarily to confirm the handler compiles, then re-add it with the explicit `state = ...` once the real state type is known."],"exampleFix":"// before\n#[debug_handler]\nasync fn handler(db: State<Db>, cfg: State<Cfg>) {}\n\n// after\n#[debug_handler(state = AppState)]\nasync fn handler(db: State<Db>, cfg: State<Cfg>) {}","handlingStrategy":"validation","validationCode":"// Before annotating, confirm exactly one State<T> appears (or name the state).\n// In a separate module, a quick lint helper (nightly-only clippy-style):\n//\n// manual check: grep your handler signature for `State<` occurrences;\n// count distinct inner types. If >1, you MUST pass `state = ...`.\n//\n// Example assertion that documents the intended single state type:\nfn _assert_single_state(_a: &State<AppState>, _b: &State<AppState>) {}\n// If the handler actually takes State<OtherState>, this fn's call site\n// fails to compile, surfacing the mismatch before #[debug_handler] does.","typeGuard":"// Compile-time guard: force every State<_> in the handler to share the same inner type.\n// (Forces a clear error at the call site rather than inside macro expansion.)\ntrait SameState {}\nimpl SameState for () {}\nfn require_single_state<S>() where S: SameState {}\n\n// Usage in tests:\n#[test]\nfn handler_state_is_unambiguous() {\n    // If you ever add State<OtherState>, this stops compiling once you\n    // add a corresponding bound here.\n    require_single_state::<AppState>();\n}","tryCatchPattern":null,"preventionTips":["Default to writing `#[debug_handler(state = AppState)]` even when inference would succeed; explicit state is robust to future field additions.","Keep handlers depending on a single top-level AppState and obtain sub-states via `FromRef`, so at most one `State<T>` inner type appears.","Treat a second `State<T>` field as a code smell: consider a dedicated sub-extractor struct derived with `FromRequestParts(state(AppState))`.","Run `cargo check` after every handler signature change; this error is compile-time and surfaces immediately."],"tags":["rust","axum","proc-macro","debug-handler","state-inference","compile-time"],"analyzedSha":"c9a911b7999de50e9e5023942ca072e9725ae943","analyzedAt":"2026-08-06T01:08:56.656Z","schemaVersion":2}