clockworklabs/SpacetimeDB · error · ArgumentException

Cannot nest router at

Error message

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

What it means

Router.Nest(path, subRouter) merges a sub-router under a prefix, but first checks that NO already-registered route path starts with the nest prefix (ordinal string StartsWith). If any existing route lies under (or equals) the prefix, it throws this ArgumentException. Note the check is a plain string prefix test, so Nest("/", sub) conflicts with every existing route, and Nest after registering overlapping paths always fails — routers are immutable, registration order matters.

Solutions

  1. Nest BEFORE registering any routes under that prefix: Router.New().Nest("/api", sub).Get("/users", h)
  2. Pick a fresh prefix that no existing route starts with
  3. Remove the now-duplicated flat routes that the sub-router provides
  4. Remember Nest("/", ...) can only be the very first operation on an empty router

Example fix

// before - flat route already occupies the /api namespace
var router = Router.New()
    .Get("/api/users", listUsers)         // registered first
    .Nest("/api", apiSubRouter);           // throws: /api/users starts with /api

// after - nest first, then non-overlapping routes
var router = Router.New()
    .Nest("/api", apiSubRouter)            // router is still empty here
    .Get("/users", listUsers);
Defensive patterns

Strategy: try-catch

Try / catch

try { return router.Nest(prefix, subRouter); }
catch (ArgumentException ae) when (ae.ParamName == "path" && ae.Message.Contains("overlap"))
{
    // registration-order bug: nest earlier or choose a free prefix;
    // fail loudly in dev so route tables get fixed, don't silently drop the sub-router
    throw new InvalidOperationException($"Route setup order invalid for nest at {prefix}", ae);
}

Prevention

When it happens

Trigger: Calling router.Get("/api/users", h).Nest("/api", sub) — /api/users starts with /api; or Nest("/", sub) once the router has any route at all; or nesting two sub-routers whose prefixes overlap.

Common situations: Composing routers from feature modules where a feature already registered routes under the prefix later used for nesting; adding a catch-all root nest at the end of setup instead of the beginning; refactoring flat routes into nested routers without removing the originals.

Related errors


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

Appendix: 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 6dee26c6ef)