clockworklabs/SpacetimeDB · error

Cannot nest router at `{path}`; existing routes overlap with

Error message

Cannot nest router at `{path}`; existing routes overlap with nested path

What it means

Router::nest panics when the parent router already has any handler whose path starts with the nest prefix, because merging the sub-router would produce two handlers under the same subtree. The check is deliberately strict (see the FIXME in the source): even non-conflicting sub-paths trigger it.

Source

Thrown at crates/bindings/src/http.rs:349

        self.add_route(MethodOrAny::Any, path, handler)
    }

    /// Causes requests which start with `path` to be processed by `sub_router`.
    ///
    /// `sub_router` will be used by stripping the leading `path` from the path of the request.
    ///
    /// Panics if `self` already has any handlers registered on paths which start with `path`.
    ///
    /// Panics if the `path` is [invalid](Self#paths).
    pub fn nest(self, path: impl Into<String>, sub_router: Self) -> Self {
        let path = path.into();
        assert_valid_path(&path);

        // FIXME: either this check is too restrictive, or the checks in the other methods are too lenient.
        // Do we want it to be the case that the `sub_router` effectively takes ownership of the whole route below `path`,
        // or just the routes it actually contains?
        if self.routes.iter().any(|route| route.path.starts_with(&path)) {
            panic!("Cannot nest router at `{path}`; existing routes overlap with nested path");
        }

        let mut merged = self;
        for route in sub_router.routes {
            let nested_path = join_paths(&path, &route.path);
            merged = merged.add_route(route.method, nested_path, route.handler);
        }
        merged
    }

    /// Combines all of the routes in `self` and `other_router` into a single [`Router`].
    ///
    /// Panics if any of the routes in `self` conflict with any of the routes in `other_router`.
    pub fn merge(self, other_router: Self) -> Self {
        let mut merged = self;
        for route in other_router.routes {
            merged = merged.add_route(route.method, route.path, route.handler);
        }

View on GitHub (pinned to 6dee26c6ef)

Solutions

  1. Nest first: call nest("/api", sub) before adding any top-level routes under /api.
  2. Move the offending parent route into the sub-router (sub.route(GET, "/health", h)) so the whole subtree has one owner.
  3. Nest at a prefix that does not prefix-match any existing route path.

Example fix

// before
let r = Router::new()
    .route(Method::GET, "/api/health", health)
    .nest("/api", api_router); // panics: /api/health starts with /api

// after: subtree owned entirely by the nested router
let api_router = api_router.route(Method::GET, "/health", health);
let r = Router::new().nest("/api", api_router);
Defensive patterns

Strategy: validation

Validate before calling

let prefix = "/api";
let taken: Vec<String> = vec!["/api/health".into()]; // your registry of registered paths
assert!(!taken.iter().any(|p| p.starts_with(prefix)), "cannot nest at {prefix}");

Prevention

When it happens

Trigger: Registering a route under the prefix before nesting, e.g. router.route(GET, "/api/health", h) followed by router.nest("/api", sub_router); nesting two sub-routers at overlapping prefixes such as "/api" then "/api/v2".

Common situations: Composing routers in library crates where each crate registers a few routes and one also nests; refactoring flat routes into nested sub-routers incrementally.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20). Data as JSON: /api/errors/c72489b0903f6ef0. Report an issue: GitHub.