clockworklabs/SpacetimeDB · error · TypeError
Route conflict for `${path}`
Error message
Route conflict for `${path}` What it means
Router.addRoute (backing get/post/put/delete/.../any) rejects a candidate that overlaps an existing route: same path string AND matching methods, where 'Any' on either side matches everything, identical methods match, and extension (custom) HTTP methods match by their string value. An overlapping registration throws TypeError('Route conflict for `<path>`') at module build time.
Source
Thrown at crates/bindings-typescript/src/server/http_handlers.ts:355
for (const route of otherRouter.#routes) {
merged = merged.addRoute(route.method, route.path, route.handler);
}
return merged;
}
intoRoutes() {
return this.#routes.slice();
}
private addRoute(
method: MethodOrAny,
path: string,
handler: HttpHandlerExport<any>
) {
assertValidPath(path);
const candidate = { method, path, handler };
if (this.#routes.some(route => routesOverlap(route, candidate))) {
throw new TypeError(`Route conflict for \`${path}\``);
}
return new Router([...this.#routes, candidate]);
}
}
export function makeHttpHandlerExport<S extends UntypedSchemaDef>(
ctx: SchemaInner,
opts: HttpHandlerOpts | undefined,
fn: HandlerFn<S>
): HttpHandlerExport<S> {
const handlerExport: HttpHandlerExport<S> = Object.assign(
(...args: Parameters<HandlerFn<S>>) => fn(...args),
{
[httpHandlerFn]: fn,
[exportContext]: ctx,
[registerExport](ctx: SchemaInner, exportName: string) {
if (exportedHttpHandlerObjects.has(handlerExport)) {
throw new TypeError(View on GitHub (pinned to 524b4487d9)
Solutions
- Delete or rename the duplicate registration so each method+path pair is unique
- Replace a specific route + .any() pair on one path with a single .any() or distinct paths
- Search the codebase for the exact path string to find both registration sites, which often live in different modules merged via nest
Example fix
// before
router.get('/health', healthA);
router.get('/health', healthB); // TypeError: Route conflict
// after
router.get('/health', healthA);
router.get('/status', healthB); Defensive patterns
Strategy: validation
Validate before calling
function findRouteConflict(router: Router, method: string, path: string): boolean {
return router.intoRoutes().some(
r => r.path === path && (r.method.tag === 'Any' || method === 'ANY' || r.method.value === method)
);
}
// check before adding:
if (findRouteConflict(router, 'GET', '/health')) throw new Error('duplicate route'); Prevention
- Define each route exactly once and centralize route registration per path
- Avoid mixing .any() with specific methods on the same path
- Write a startup assertion that walks intoRoutes() and fails on duplicates with a descriptive message
When it happens
Trigger: Registering the same method+path twice (two .get('/health', ...)); combining router.any('/x', h) with router.get('/x', h) since Any overlaps every method; registering the same custom extension method value twice on one path.
Common situations: Merging routers or files that both define the same route; adding .any() as a fallback after specific handlers on the same path (ambiguous routing is rejected by design); duplicated route constants after copy-paste.
Related errors
- Cannot nest router at `${path}`; existing routes overlap wit
- Route paths must start with `/`: ${path}
- Route paths may contain only ${ACCEPTABLE_ROUTE_PATH_CHARS_H
- HTTP handler '${exportName}' was exported more than once
- HTTP router references unknown handler `{route.HandlerFuncti
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/8aeb88bf6a02f42f.
Report an issue: GitHub.