tokio-rs/axum · error · syn::Error
Missing path: #[typed_path("/foo/bar")]
Error message
Missing path: #[typed_path("/foo/bar")] What it means
Thrown by #[derive(TypedPath)] (typed_path.rs:26). The derive parses attributes named typed_path via parse_attrs; the Attrs::parse parser only fills the path field when it peeks a LitStr. If after parsing, path is None (no #[typed_path("...")] attribute present, or only a rejection(...) sub-attribute was given with no string literal), path.ok_or_else emits this error. (Generics, or a non-struct item, produce different errors upstream.)
Solutions
- Add the attribute with a route literal: #[typed_path("/your/route")].
- Make sure the route starts with '/' (a missing leading slash raises a separate error).
- If you only want a rejection override, still include the path: #[typed_path("/foo", rejection(MyRejection))].
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
// Safe invocation pattern: every TypedPath derive needs a `#[typed_path("...")]` literal.
#[derive(TypedPath, Deserialize)]
#[typed_path("/users/{id}")] // route literal is mandatory and must start with '/'
struct UsersShow {
id: u32,
}
// With a custom rejection, the path literal is still required:
#[derive(TypedPath, Deserialize)]
#[typed_path("/users/{id}", rejection(MyRejection))]
struct UsersShow {
id: u32,
} Prevention
- Always pair #[derive(TypedPath)] with a #[typed_path("/...")] attribute in the same commit.
- Spell the attribute exactly typed_path; other names (path, route) are silently ignored and cause this error.
- Ensure the route begins with '/'; otherwise a separate 'paths must start with a `/`' error fires.
- Do not put generics on a TypedPath struct — generics are rejected with a different error upstream.
When it happens
Trigger: #[derive(TypedPath)] struct MyPath { ... } with no #[typed_path("...")] attribute. Also triggered by #[typed_path(rejection(Foo))] without a path string, since the rejection keyword does not set path.
Common situations: Forgetting the typed_path attribute entirely. Misspelling it (e.g. #[path("...")]). Adding #[derive(TypedPath)] alongside other derives and assuming the route comes from elsewhere (e.g. from an axum::Router::route call). Only supplying a rejection type and omitting the route literal.
Related errors
- Typed paths for unit structs cannot contain captures
- can't infer state type, please add #
- can't infer state type, please add set it explicitly, as in…
- missing #[from_request(via(...))]
AI-assisted analysis of tokio-rs/axum@151cd5c123 (2026-08-11).
Data as JSON: /api/errors/17c6f51600bb637a.
Report an issue: GitHub.
Appendix: 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 151cd5c123)