tokio-rs/axum · error · compile_error
can't infer state type, please add set it explicitly, as in
Error message
can't infer state type, please add set it explicitly, as in `#[axum_macros::debug_{kind}(state = MyStateType)]` What it means
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`.
Source
Thrown at axum-macros/src/debug_handler.rs:38
check_output_impls_into_response(item_fn)
} else {
check_output_tuples
};
// If the function is generic, we can't reliably check its inputs or whether the future it
// returns is `Send`. Skip those checks to avoid unhelpful additional compiler errors.
let check_inputs_and_future_send = if item_fn.sig.generics.params.is_empty() {
let mut err = None;
if state_ty.is_none() {
let state_types_from_args = state_types_from_args(item_fn);
#[allow(clippy::comparison_chain)]
if state_types_from_args.len() == 1 {
state_ty = state_types_from_args.into_iter().next();
} else if state_types_from_args.len() > 1 {
err = Some(
syn::Error::new(
Span::call_site(),
format!(
"can't infer state type, please add set it explicitly, as in \
`#[axum_macros::debug_{kind}(state = MyStateType)]`"
),
)
.into_compile_error(),
);
}
}
err.unwrap_or_else(|| {
let state_ty = state_ty.unwrap_or_else(|| syn::parse_quote!(()));
let check_future_send = check_future_send(item_fn, kind);
if let Some(check_input_order) = check_input_order(item_fn, kind) {
quote! {View on GitHub (pinned to c9a911b799)
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.
Example fix
// before
#[debug_handler]
async fn handler(db: State<Db>, cfg: State<Cfg>) {}
// after
#[debug_handler(state = AppState)]
async fn handler(db: State<Db>, cfg: State<Cfg>) {} Defensive patterns
Strategy: validation
Validate before calling
// Before annotating, confirm exactly one State<T> appears (or name the state).
// In a separate module, a quick lint helper (nightly-only clippy-style):
//
// manual check: grep your handler signature for `State<` occurrences;
// count distinct inner types. If >1, you MUST pass `state = ...`.
//
// Example assertion that documents the intended single state type:
fn _assert_single_state(_a: &State<AppState>, _b: &State<AppState>) {}
// If the handler actually takes State<OtherState>, this fn's call site
// fails to compile, surfacing the mismatch before #[debug_handler] does. Type guard
// Compile-time guard: force every State<_> in the handler to share the same inner type.
// (Forces a clear error at the call site rather than inside macro expansion.)
trait SameState {}
impl SameState for () {}
fn require_single_state<S>() where S: SameState {}
// Usage in tests:
#[test]
fn handler_state_is_unambiguous() {
// If you ever add State<OtherState>, this stops compiling once you
// add a corresponding bound here.
require_single_state::<AppState>();
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- can't infer state type, please add `#[{attr_name}(state = My
- missing `#[from_request(via(...))]`
- Missing path: `#[typed_path("/foo/bar")]`
- Typed paths for unit structs cannot contain captures
AI-assisted analysis of tokio-rs/axum@c9a911b799 (2026-08-06).
Data as JSON: /data/errors/dc3183732d304777.json.
Report an issue: GitHub.