{"id":"c42410fb7e27174e","repo":"actix/actix-web","slug":"scope-macro-must-be-attached-to-a-module","errorCode":null,"errorMessage":"#[scope] macro must be attached to a module","messagePattern":"#\\[scope\\] macro must be attached to a module","errorType":"exception","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"actix-web-codegen/src/scope.rs","lineNumber":45,"sourceCode":"            err.span(),\n            \"argument to scope macro is not a string literal, expected: #[scope(\\\"/prefix\\\")]\",\n        )\n    })?;\n\n    let scope_prefix_value = scope_prefix.value();\n\n    if scope_prefix_value.ends_with('/') {\n        // trailing slashes cause non-obvious problems\n        // it's better to point them out to developers rather than\n\n        return Err(syn::Error::new(\n            scope_prefix.span(),\n            \"scopes should not have trailing slashes; see https://docs.rs/actix-web/4/actix_web/struct.Scope.html#avoid-trailing-slashes\",\n        ));\n    }\n\n    let mut module = syn::parse::<syn::ItemMod>(input).map_err(|err| {\n        syn::Error::new(err.span(), \"#[scope] macro must be attached to a module\")\n    })?;\n\n    // modify any routing macros (method or route[s]) attached to\n    // functions by prefixing them with this scope macro's argument\n    if let Some((_, items)) = &mut module.content {\n        for item in items {\n            if let syn::Item::Fn(fun) = item {\n                fun.attrs = fun\n                    .attrs\n                    .iter()\n                    .map(|attr| modify_attribute_with_scope(attr, &scope_prefix_value))\n                    .collect();\n            }\n        }\n    }\n\n    Ok(module.to_token_stream().into())\n}","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/actix/actix-web/blob/937960ca67f20e14ffe2a075bf6d4593502be12c/actix-web-codegen/src/scope.rs#L27-L63","documentation":"`#[scope]` is designed to prefix routes defined inside a module. At actix-web-codegen/src/scope.rs:44-46 the macro parses the item as `syn::ItemMod`; if it isn't a module the parse error is replaced with this clear message.","triggerScenarios":"Placing `#[scope(\"/api\")]` on a function, struct, or `impl` block instead of a `mod`.","commonSituations":"Expecting `scope` to work like the runtime `Scope::new()` builder on a single handler, rather than as a module-level macro.","solutions":["Move the annotated routes into a module and put `#[scope(...)]` on the `mod`.","If you want runtime prefixing for a single route, use `web::scope(\"/api\").route(...)` in your `App` setup instead of the macro."],"exampleFix":"// before\n#[scope(\"/api\")]\nasync fn handler() -> HttpResponse { ... }\n\n// after\n#[scope(\"/api\")]\nmod api {\n    use super::*;\n    #[get(\"/users\")]\n    pub async fn users() -> HttpResponse { ... }\n}","handlingStrategy":"validation","validationCode":"// Compile-time only. #[scope(...)] must decorate a `mod` item.\n// If you need runtime prefixing of a single handler, use:\n//   web::scope(\"/api\").route(\"/x\", web::get().to(handler))\n// in your App configuration instead.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Reserve #[scope] for modules containing multiple route handlers.","Use the runtime Scope builder for ad-hoc single-route prefixes."],"tags":["rust","actix","actix-web","macro","compile-time","routing"],"analyzedSha":"937960ca67f20e14ffe2a075bf6d4593502be12c","analyzedAt":"2026-08-06T01:15:46.978Z","schemaVersion":2}