clockworklabs/SpacetimeDB · error · TypeError

HTTP handler '${exportName}' was exported more than once

Error message

HTTP handler '${exportName}' was exported more than once

What it means

HTTP handlers are created via makeHttpHandlerExport and must be registered exactly once when the module's exports are processed. Each handler object is tracked in the module-level exportedHttpHandlerObjects WeakSet; if the same handler object is registered again under another export name, the SDK throws, because one handler object must map to exactly one export name and schema entry.

Source

Thrown at crates/bindings-typescript/src/server/http_handlers.ts:373

      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(
            `HTTP handler '${exportName}' was exported more than once`
          );
        }
        exportedHttpHandlerObjects.add(handlerExport);
        registerHttpHandler(ctx, exportName, fn, opts);
        ctx.httpHandlerExports.set(
          handlerExport as HttpHandlerExport<UntypedSchemaDef>,
          exportName
        );
      },
    }
  );
  return handlerExport;
}

export function makeHttpRouterExport(
  ctx: SchemaInner,
  router: Router

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Create a separate handler per export name with its own httpHandler/makeHttpHandlerExport call instead of re-exporting the same object
  2. Remove the redundant re-export so each handler is exported from exactly one module under exactly one name
  3. If hot-module-reload triggers it during development, restart the dev server so the export table is rebuilt once

Example fix

// before
// users.ts
export const getUser = makeHttpHandler(...);
// index.ts
export { getUser } from './users';
export { getUser as getUserV2 } from './users'; // second registration throws

// after: one handler object per export name
export { getUser } from './users';
export const getUserV2 = makeHttpHandler(/* own fn */);
Defensive patterns

Strategy: validation

Validate before calling

// convention check at module setup: one export name per handler object
const registered = new WeakSet<object>();
function assertSingleExport(handlerObj: object, name: string): void {
  if (registered.has(handlerObj)) throw new Error(`handler re-exported: ${name}`);
  registered.add(handlerObj);
}

Prevention

When it happens

Trigger: Exporting the same handler object under two names (including via barrel-file re-exports or aliases); registering one handler object in two submodules; dev-server hot reload re-running export registration on the same objects.

Common situations: index.ts doing `export * from './users'` plus `export { getUser as getUser2 } from './users'`; sharing a handler constant between submodules to avoid duplication; bundlers that instantiate module code twice.

Related errors


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