clockworklabs/SpacetimeDB · error · TypeError
Route paths may contain only ${ACCEPTABLE_ROUTE_PATH_CHARS_H
Error message
Route paths may contain only ${ACCEPTABLE_ROUTE_PATH_CHARS_HUMAN_DESCRIPTION}: ${path} What it means
assertValidPath also enforces a strict character whitelist for route paths: lowercase a-z, digits 0-9, '-', '_', '~' and '/'. These are the RFC 3986 unreserved characters, so routes match without percent-decoding ambiguity. Any other character (uppercase letters, ':', '{', '.', '*', spaces) throws a TypeError listing the allowed set.
Source
Thrown at crates/bindings-typescript/src/server/http_handlers.ts:107
}
function characterIsAcceptableForRoutePath(c: string) {
return (
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
c === '-' ||
c === '_' ||
c === '~' ||
c === '/'
);
}
function assertValidPath(path: string) {
if (path !== '' && !path.startsWith('/')) {
throw new TypeError(`Route paths must start with \`/\`: ${path}`);
}
if (![...path].every(characterIsAcceptableForRoutePath)) {
throw new TypeError(
`Route paths may contain only ${ACCEPTABLE_ROUTE_PATH_CHARS_HUMAN_DESCRIPTION}: ${path}`
);
}
}
function routesOverlap(a: RouteSpec, b: RouteSpec) {
const methodsMatch = (left: HttpMethod, right: HttpMethod) => {
if (left.tag !== right.tag) {
return false;
}
if (left.tag === 'Extension' && right.tag === 'Extension') {
return left.value === right.value;
}
return true;
};
return (
a.path === b.path &&View on GitHub (pinned to 524b4487d9)
Solutions
- Replace path parameters with a static segment and read identifiers from the query string or request body inside the handler
- Lowercase the whole path and strip unsupported characters
- If per-id routes are required, parse ctx.request.uri inside one handler and dispatch manually
Example fix
// before
router.get('/users/:id', getUser);
// after
router.get('/users', getUser); // read ?id=... from the query string inside the handler Defensive patterns
Strategy: validation
Validate before calling
const ROUTE_PATH_RE = /^[a-z0-9\-_~/]*$/;
function isValidRoutePath(path: string): boolean {
return (path === '' || path.startsWith('/')) && ROUTE_PATH_RE.test(path);
}
// reject early with a clear message:
if (!isValidRoutePath(p)) throw new Error(`bad route path: ${p}`); Type guard
const isStaticRoutePath = (p: string): p is string =>
(p === '' || p.startsWith('/')) && /^[a-z0-9\-_~/]*$/.test(p); Prevention
- Remember this router has no path parameters: design identifiers as query strings or body fields
- Keep route paths lowercase and limited to unreserved characters from the start
- Lint route definitions in CI with the same regex the runtime uses
When it happens
Trigger: Writing REST-style parameterized paths like '/users/:id' or '/users/{id}' (path parameters are not supported by this router); camelCase or uppercase segments such as '/api/Users'; extension routes like '/sitemap.xml'.
Common situations: Coming from Express/Koa/Fastify where ':param' syntax is standard; generated route tables containing uppercase or dots; assuming dynamic path segments exist because other frameworks offer them.
Related errors
- Route paths must start with `/`: ${path}
- Cannot nest router at `${path}`; existing routes overlap wit
- Route conflict for `${path}`
- Cannot convert ${typeof value} to ${what}: expected bigint,
- `fromCounterV7` requires `randomBytes.length == 4`
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/f066af88b129cda3.
Report an issue: GitHub.