tokio-rs/axum · error · compile_error
Missing path: `#[typed_path("/foo/bar")]`
Error message
Missing path: `#[typed_path("/foo/bar")]` What it means
Thrown by `#[derive(TypedPath)]` when the struct has no `#[typed_path("...")]` attribute supplying the route template. `TypedPath` requires a `const PATH: &str` (see the generated `impl ::axum_extra::routing::TypedPath` at typed_path.rs:112), and the macro has nothing to put there, so at line 25 it converts `None` into this error. The message text is taken verbatim from the `ok_or_else` at line 25-30.
Source
Thrown at axum-macros/src/typed_path.rs:26
let ItemStruct {
attrs,
ident,
generics,
fields,
..
} = item_struct;
if !generics.params.is_empty() || generics.where_clause.is_some() {
return Err(syn::Error::new_spanned(
generics,
"`#[derive(TypedPath)]` doesn't support generics",
));
}
let Attrs { path, rejection } = crate::attr_parsing::parse_attrs("typed_path", attrs)?;
let path = path.ok_or_else(|| {
syn::Error::new(
Span::call_site(),
"Missing path: `#[typed_path(\"/foo/bar\")]`",
)
})?;
let rejection = rejection.map(second);
match fields {
syn::Fields::Named(_) => {
let segments = parse_path(&path)?;
Ok(expand_named_fields(
ident,
&path,
&segments,
rejection.as_ref(),
))
}
syn::Fields::Unnamed(fields) => {View on GitHub (pinned to c9a911b799)
Solutions
- Add the helper attribute immediately above the struct: `#[typed_path("/users/{id}")]`.
- Confirm the attribute name is exactly `typed_path` (snake_case) and the value is a single string literal starting with `/`.
- If you intended a route with no captures on a unit struct, supply a static path like `#[typed_path("/health")]`.
- If you only wanted serde deserialization of path params, drop `#[derive(TypedPath)]` and use `Path<YourStruct>` directly in the handler.
Example fix
// before
#[derive(TypedPath, Deserialize)]
struct MyPath { id: u32 }
// after
#[derive(TypedPath, Deserialize)]
#[typed_path("/users/{id}")]
struct MyPath { id: u32 } Defensive patterns
Strategy: validation
Validate before calling
// The derive REQUIRES the helper attribute. Add a CI grep to catch missing it:
// rg --type rust '#\[derive\([^)]*TypedPath' -A 5 | \
// rg -B1 -A4 'struct ' | rg -v 'typed_path\(' && echo FAIL || true
//
// Convention: define a tiny macro_rules! that forces both pieces together:
macro_rules! typed_route {
($name:ident, $path:literal, $($body:tt)*) => {
#[derive(::axum_macros::TypedPath)]
#[typed_path($path)]
$($body)*
struct $name;
};
}
// Usage: typed_route!(Health, "/health"); // impossible to forget the attribute Type guard
// Compile-time check that TypedPath::PATH is defined (fails if derive was skipped
// or attribute missing because the impl wouldn't be generated).
const _: fn() = || {
fn assert_typed_path<T: ::axum_extra::routing::TypedPath>() {}
assert_typed_path::<MyPath>();
// Also assert the path constant equals what you expect:
const EXPECTED: &str = "/users/{id}";
const _: () = assert!(MyPath::PATH == EXPECTED);
}; Prevention
- Always write `#[typed_path("...")]` on the line immediately above `#[derive(TypedPath)]` so the two stay visually paired.
- Use the `typed_route!` macro above for unit routes so the attribute cannot be omitted.
- Add a `const _: () = assert!(MyPath::PATH == "/expected");` test for every typed route to catch missing attributes at compile time.
- Run `cargo check` after adding any new `TypedPath` derive; this error is purely compile-time.
When it happens
Trigger: Annotated a struct with `#[derive(TypedPath)]` (or `#[axum_macros::TypedPath]`) but forgot the accompanying `#[typed_path("/some/route")]` helper attribute. The parse loop at typed_path.rs:62-81 leaves `path = None`, and the `path.ok_or_else(...)` at line 25 produces the compile error before any field/generation code runs.
Common situations: First-time use of `TypedPath` where the developer assumes the derive alone is sufficient (as it is for many serde/clone derives). Renaming or deleting the helper attribute during a refactor. Splitting attribute lists across macros (e.g. `#[derive(Deserialize, TypedPath)]` with only `#[serde(...)]` present). Upgrading axum-extra and missing the migration note that the path attribute is mandatory.
Related errors
- Typed paths for unit structs cannot contain captures
- can't infer state type, please add `#[{attr_name}(state = My
- missing `#[from_request(via(...))]`
- can't infer state type, please add set it explicitly, as in
AI-assisted analysis of tokio-rs/axum@c9a911b799 (2026-08-06).
Data as JSON: /data/errors/8ce3b6976cbfaf87.json.
Report an issue: GitHub.