clockworklabs/SpacetimeDB · error · ArgumentException

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(path, subRouter) refuses to mount a sub-router when any route already registered on the current router has a path that string-starts-with the nest prefix. Router is immutable and fluent (each Get/Post/Nest returns a new router with accumulated routes), so the check covers everything built earlier in the chain. The check is a literal StartsWith, not segment-aware, so '/users-all' also blocks nesting at '/users'.

Source

Thrown at crates/bindings-csharp/Runtime/Router.cs:58

    public Router Delete(string path, Handler handler) =>
        AddRoute(new MethodOrAny.Method(new Internal.HttpMethod.Delete(default)), path, handler);

    public Router Post(string path, Handler handler) =>
        AddRoute(new MethodOrAny.Method(new Internal.HttpMethod.Post(default)), path, handler);

    public Router Patch(string path, Handler handler) =>
        AddRoute(new MethodOrAny.Method(new Internal.HttpMethod.Patch(default)), path, handler);

    public Router Any(string path, Handler handler) =>
        AddRoute(new MethodOrAny.Any(default), path, handler);

    public Router Nest(string path, Router subRouter)
    {
        AssertValidPath(path);
        if (routes.Exists(route => route.Path.StartsWith(path, StringComparison.Ordinal)))
        {
            throw new ArgumentException(
                $"Cannot nest router at `{path}`; existing routes overlap with nested path",
                nameof(path)
            );
        }

        var merged = CloneRoutes();
        foreach (var route in subRouter.routes)
        {
            var nestedPath = JoinPaths(path, route.Path);
            AddRoute(merged, route.Method, nestedPath, route.HandlerFunction);
        }

        return new Router(merged);
    }

    public Router Merge(Router otherRouter)
    {
        var merged = CloneRoutes();

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Reorder the chain: call Nest(path, subRouter) first, then register routes that live outside the nested prefix
  2. Choose a nest prefix that no existing route path starts with (audit the chain built so far)
  3. Restructure: build sub-routers against Router.New() and Merge them, keeping top-level prefixes disjoint

Example fix

// before
var router = Router.New()
    .Get("/users", listUsers)   // blocks the nest below
    .Nest("/users", userRouter);

// after
var router = Router.New()
    .Nest("/users", userRouter) // nest first
    .Get("/health", health);    // register only non-overlapping prefixes after
Defensive patterns

Strategy: validation

Validate before calling

// Track the paths you registered, then check before nesting:
bool SafeToNest(HashSet<string> registered, string prefix) =>
    !registered.Any(p => p.StartsWith(prefix, StringComparison.Ordinal));

Try / catch

try { router = router.Nest(prefix, sub); }
catch (ArgumentException e) when (e.Message.Contains("overlap with nested path"))
{ /* restructure: nest first or pick a disjoint prefix */ }

Prevention

When it happens

Trigger: Calling .Get("/users", h) (or any method) before .Nest("/users", subRouter) on the same chain; also plain prefix collisions such as an existing '/v1' route when nesting at '/v1/admin'. Nesting at '/' conflicts with every non-empty existing path.

Common situations: Refactoring a flat route list into nested routers while leaving one route under the old prefix; additive route building where a catch-all or sibling route shares the prefix string; porting route tables from frameworks where nesting is resolved per-segment.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/abe886a67b87265f. Report an issue: GitHub.