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

  1. Delete or rename the duplicate registration so each method+path pair is unique
  2. Replace a specific route + .any() pair on one path with a single .any() or distinct paths
  3. 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

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


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