actix/actix-web · error · syn::Error

scopes should not have trailing slashes; see https://docs.rs

Error message

scopes should not have trailing slashes; see https://docs.rs/actix-web/4/actix_web/struct.Scope.html#avoid-trailing-slashes

What it means

Scope prefixes must not end with a `/`. At actix-web-codegen/src/scope.rs:34-42 the macro rejects a trailing slash because it produces non-obvious routing bugs (double slashes when concatenated with route paths). The message links to the official guidance on avoiding trailing slashes.

Source

Thrown at actix-web-codegen/src/scope.rs:38

            Span::call_site(),
            "missing arguments for scope macro, expected: #[scope(\"/prefix\")]",
        ));
    }

    let scope_prefix = syn::parse::<syn::LitStr>(args.clone()).map_err(|err| {
        syn::Error::new(
            err.span(),
            "argument to scope macro is not a string literal, expected: #[scope(\"/prefix\")]",
        )
    })?;

    let scope_prefix_value = scope_prefix.value();

    if scope_prefix_value.ends_with('/') {
        // trailing slashes cause non-obvious problems
        // it's better to point them out to developers rather than

        return Err(syn::Error::new(
            scope_prefix.span(),
            "scopes should not have trailing slashes; see https://docs.rs/actix-web/4/actix_web/struct.Scope.html#avoid-trailing-slashes",
        ));
    }

    let mut module = syn::parse::<syn::ItemMod>(input).map_err(|err| {
        syn::Error::new(err.span(), "#[scope] macro must be attached to a module")
    })?;

    // modify any routing macros (method or route[s]) attached to
    // functions by prefixing them with this scope macro's argument
    if let Some((_, items)) = &mut module.content {
        for item in items {
            if let syn::Item::Fn(fun) = item {
                fun.attrs = fun
                    .attrs
                    .iter()
                    .map(|attr| modify_attribute_with_scope(attr, &scope_prefix_value))

View on GitHub (pinned to 937960ca67)

Solutions

  1. Remove the trailing slash from the scope prefix: `#[scope("/api")]`.
  2. Leave trailing slashes off inner route paths too; rely on the scope prefix + route path joining.

Example fix

// before
#[scope("/api/")]
mod api {
    #[get("/users")]
    async fn users() -> HttpResponse { ... }
}

// after
#[scope("/api")]
mod api {
    #[get("/users")]
    async fn users() -> HttpResponse { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

// Compile-time only. Reject trailing slashes in scope prefixes.
// Quick check: ensure the prefix does not end with '/'.
fn no_trailing_slash(p: &str) -> bool { !p.ends_with('/') }
// assert!(no_trailing_slash("/api"));
// assert!(!no_trailing_slash("/api/"));

Prevention

When it happens

Trigger: `#[scope("/api/")]` — prefix ending in `/`. The macro then concatenates this with inner route paths like `"/users"`, yielding `"/api//users"`.

Common situations: Copying a base URL with a trailing slash from a config/browser, or assuming the framework normalizes slashes.

Related errors


AI-assisted analysis of actix/actix-web@937960ca67 (2026-08-06). Data as JSON: /data/errors/b73af54e6da4b828.json. Report an issue: GitHub.