{"record":{"id":"99c647cb61d4c889","repo":"yewstack/yew","slug":"expected-enum-found-struct","errorCode":null,"errorMessage":"expected enum, found struct","messagePattern":"expected enum, found struct","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"packages/yew-router-macro/src/routable_derive.rs","lineNumber":44,"sourceCode":"    }\n    params\n}\n\npub struct Routable {\n    ident: Ident,\n    ats: Vec<LitStr>,\n    variants: Punctuated<Variant, syn::token::Comma>,\n    not_found_route: Option<Ident>,\n}\n\nimpl Parse for Routable {\n    fn parse(input: ParseStream) -> syn::Result<Self> {\n        let DeriveInput { ident, data, .. } = input.parse()?;\n\n        let data = match data {\n            Data::Enum(data) => data,\n            Data::Struct(s) => {\n                return Err(syn::Error::new(\n                    s.struct_token.span(),\n                    \"expected enum, found struct\",\n                ));\n            }\n            Data::Union(u) => {\n                return Err(syn::Error::new(\n                    u.union_token.span(),\n                    \"expected enum, found union\",\n                ));\n            }\n        };\n\n        let (not_found_route, ats) = parse_variants_attributes(&data.variants)?;\n\n        Ok(Self {\n            ident,\n            variants: data.variants,\n            ats,","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/yewstack/yew/blob/0e4a05472fac4e5fce1befe60fa4a1e43a36b6a3/packages/yew-router-macro/src/routable_derive.rs#L26-L62","documentation":"The #[derive(Routable)] macro (packages/yew-router-macro/src/routable_derive.rs:44) only accepts enums: a route model is a closed set of variants, each mapped to a URL pattern by an #[at(\"...\")] attribute. Applying the derive to a struct is rejected at the struct token with 'expected enum, found struct'.","triggerScenarios":"Annotating a struct with #[derive(Routable)], e.g. #[derive(Routable, Clone)] struct Routes { home: () }. The derive input parser rejects Data::Struct before any route processing.","commonSituations":"Coming from routers where routes are a config struct or builder (actix, axum-style); holding route metadata in a struct and adding the derive as an afterthought; copy-pasting a derive list onto the wrong item.","solutions":["Convert the item into an enum where each variant is one route, with #[at(\"/path\")] on every variant","Use named fields on variants that capture route parameters (e.g. Post { id: u32 }) and unit variants for static routes","If the struct must stay, remove the Routable derive and map the struct from an enum route model instead"],"exampleFix":"// before\n#[derive(Routable, Clone)]\nstruct Routes {\n    home: (),\n}\n\n// after\n#[derive(Routable, Clone)]\nenum Routes {\n    #[at(\"/\")]\n    Home,\n}","handlingStrategy":"validation","validationCode":"// before deriving, confirm the item is an enum with one #[at] per variant\n// #[derive(Routable, Clone)]\n// enum Routes { #[at(\"/\")] Home, #[at(\"/about\")] { /* ... */ } About }","typeGuard":"// compile-time shape check: fails to compile if Routes is not an enum\nconst _: fn(Routes) -> Routes = |r| match r { _ => r };","tryCatchPattern":null,"preventionTips":["Reserve #[derive(Routable)] for enums only","Model each route as one variant; parameters as named fields","Put the derive on the item that represents your URL space, not on data payloads"],"tags":["rust","derive-macro","yew-router","compile-time","routing"],"backgroundTag":"derive-macro-wrong-item-type","analyzedSha":"0e4a05472fac4e5fce1befe60fa4a1e43a36b6a3","analyzedAt":"2026-08-22T21:16:31.212Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}