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

scopes should not have trailing slashes; see…

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

This compile-time error fires when the scope prefix string ends with a trailing slash (e.g., `"/api/"`). The check at line 34 detects this and rejects it because trailing slashes in scope prefixes cause non-obvious routing problems — the combined route paths end up with double slashes or unexpected matching behavior.

Solutions

  1. Remove the trailing slash from the scope prefix: `#[scope("/api")]`
  2. The individual route paths inside the scope should start with `/`: `#[get("/users")]` produces the combined path `/api/users`
  3. If the root of the scope needs to be handled, use `#[get("")]` inside the scope

Example fix

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

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

Strategy: validation

Validate before calling

// Validate the scope prefix does not end with a trailing slash.
// This is a compile-time check, but you can apply the same rule to runtime paths.
fn validate_scope_prefix(prefix: &str) -> Result<(), String> {
    if prefix.ends_with('/') {
        Err(format!("Scope prefix '{}' has a trailing slash", prefix))
    } else {
        Ok(())
    }
}

Type guard

fn has_no_trailing_slash(prefix: &str) -> bool {
    !prefix.ends_with('/')
}

Prevention

When it happens

Trigger: Writing `#[scope("/api/")]` with a trailing slash. The combined route paths would become `"/api//users"` which can lead to incorrect matches or 404s.

Common situations: Copy-pasting a URL prefix that includes a trailing slash, or following a convention from another framework where trailing slashes are the norm.

Related errors


AI-assisted analysis of actix/actix-web@4d435abc28 (2026-08-09). Data as JSON: /api/errors/b73af54e6da4b828. Report an issue: GitHub.

Appendix: 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 4d435abc28)