clockworklabs/SpacetimeDB · error · ArgumentException

Route paths may contain only {AcceptableRoutePathCharsHumanD

Error message

Route paths may contain only {AcceptableRoutePathCharsHumanDescription}: {path}

What it means

Route paths accept only ASCII lowercase letters, digits, '-', '_', '~' and '/'. AssertValidPath scans every character and throws ArgumentException listing the allowed set. Notably rejected: uppercase letters, dots, colons, and braces — this router has no path-parameter syntax, so '/users/{id}'-style paths from other frameworks are invalid here.

Source

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

        }

        return a.Method is MethodOrAny.Any
            || b.Method is MethodOrAny.Any
            || Equals(a.Method, b.Method);
    }

    private static void AssertValidPath(string path)
    {
        ArgumentNullException.ThrowIfNull(path);
        if (path.Length > 0 && path[0] != '/')
        {
            throw new ArgumentException($"Route paths must start with `/`: {path}", nameof(path));
        }
        foreach (var c in path)
        {
            if (!CharacterIsAcceptableForRoutePath(c))
            {
                throw new ArgumentException(
                    $"Route paths may contain only {AcceptableRoutePathCharsHumanDescription}: {path}",
                    nameof(path)
                );
            }
        }
    }

    private static bool CharacterIsAcceptableForRoutePath(char c) =>
        c is (>= 'a' and <= 'z') or (>= '0' and <= '9') or '-' or '_' or '~' or '/';
}

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Lowercase the whole path and strip unsupported characters
  2. Remove path-parameter placeholders — this router matches static paths only; encode the variable part in the handler or a query string instead
  3. Rewrite versioned paths like '/v1.0/users' as '/v1_0/users'

Example fix

// before
var router = Router.New().Get("/users/{id}", getUser); // '{', '}' rejected

// after
var router = Router.New().Get("/users", listUsers); // static paths only;
// read selection criteria from the request inside the handler
Defensive patterns

Strategy: validation

Validate before calling

static bool IsValidRoutePath(string path) =>
    (path.Length == 0 || path[0] == '/')
    && path.All(c => c is (>= 'a' and <= 'z') or (>= '0' and <= '9') or '-' or '_' or '~' or '/');

Try / catch

try { router = router.Get(path, handler); }
catch (ArgumentException e) when (e.Message.Contains("may contain only"))
{ /* lowercase the path, drop dots/placeholders, then retry */ }

Prevention

When it happens

Trigger: Registering "/Users" (uppercase), "/v1.0/users" (dot), "/users/{id}" or "/users/:id" (parameter placeholders), or any path containing uppercase/non-ASCII characters.

Common situations: Porting route tables from Express/ASP.NET-style routers that support parameters; versioned paths with dots; case-mismatched copies of paths.

Related errors


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