{"record":{"id":"26ec7e36aa46b93a","repo":"clockworklabs/SpacetimeDB","slug":"http-handler-exportname-was-exported-more-tha","errorCode":null,"errorMessage":"HTTP handler '${exportName}' was exported more than once","messagePattern":"HTTP handler '(.+?)' was exported more than once","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/bindings-typescript/src/server/http_handlers.ts","lineNumber":375,"sourceCode":"      throw new TypeError(`Route conflict for \\`${path}\\``);\n    }\n    return new Router([...this.#routes, candidate]);\n  }\n}\n\nexport function makeHttpHandlerExport<S extends UntypedSchemaDef>(\n  ctx: SchemaInner,\n  opts: HttpHandlerOpts | undefined,\n  fn: HandlerFn<S>\n): HttpHandlerExport<S> {\n  const handlerExport: HttpHandlerExport<S> = Object.assign(\n    (...args: Parameters<HandlerFn<S>>) => fn(...args),\n    {\n      [httpHandlerFn]: fn,\n      [exportContext]: ctx,\n      [registerExport](ctx: SchemaInner, exportName: string) {\n        if (exportedHttpHandlerObjects.has(handlerExport)) {\n          throw new TypeError(\n            `HTTP handler '${exportName}' was exported more than once`\n          );\n        }\n        exportedHttpHandlerObjects.add(handlerExport);\n        registerHttpHandler(ctx, exportName, fn, opts);\n        ctx.httpHandlerExports.set(\n          handlerExport as HttpHandlerExport<UntypedSchemaDef>,\n          exportName\n        );\n      },\n    }\n  );\n  return handlerExport;\n}\n\nexport function makeHttpRouterExport(\n  ctx: SchemaInner,\n  router: Router","sourceCodeStart":357,"sourceCodeEnd":393,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/eddf9f5014579a50d4b67630e28b6e15cad9c4af/crates/bindings-typescript/src/server/http_handlers.ts#L357-L393","documentation":"An HTTP handler created with `spacetimedb.httpHandler(...)` carries a hidden `[registerExport]` hook. When the module's exports are registered, the export object is added to a module-level `exportedHttpHandlerObjects` WeakSet; if that exact object is already in the set, this TypeError fires. In other words, one handler export object may be registered under exactly one export name, ever, per module build.","triggerScenarios":"Re-exporting the same handler object under two names (`export { handleThing }; export { handleThing as handleThingAlias };`); exporting the same handler from a shared file and re-exporting it in the module entry; registering the same handler object in two places that both run `registerExport` during one publish.","commonSituations":"Barrel-file re-exports (`export * from './handlers'` plus a manual `export { handleThing }`); aliasing an export for backwards compatibility after a rename; sharing one handler between a library submodule and the app entry module.","solutions":["Keep exactly one `export` for each handler object and delete any aliased or re-exported duplicates.","If you need two names to serve the same logic, create a second handler with `spacetimedb.httpHandler(...)` wrapping the same underlying function, and export each object once.","Search the module entry (and its re-export chain) for the handler name from the error message to find the duplicate `export` statement."],"exampleFix":"// before\nexport const handleThing = spacetimedb.httpHandler(...);\nexport { handleThing as handleThingV2 }; // second registration -> TypeError\n\n// after\nexport const handleThing = spacetimedb.httpHandler(...);\nconst thingFn = handleThing; // plain alias, not re-exported via `export`","handlingStrategy":"validation","validationCode":"// In tests or a build script: verify no export object appears under two names.\nconst seen = new Map<object, string>();\nfor (const [name, exp] of Object.entries(moduleExports)) {\n  if (typeof exp === 'function' || (typeof exp === 'object' && exp !== null)) {\n    const prev = seen.get(exp);\n    if (prev !== undefined && prev !== name) {\n      throw new Error(`handler exported twice: ${prev} and ${name}`);\n    }\n    seen.set(exp, name);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never alias-export handlers (`export { h as h2 }`); wrap the underlying fn in a new httpHandler instead.","Avoid `export *` barrel chains for the module entry; list exports explicitly.","Add a CI check that fails when an entry-module export object is reachable under two export names."],"tags":["http","exports","spacetimedb","typescript","build-time"],"backgroundTag":"duplicate-export","analyzedSha":"eddf9f5014579a50d4b67630e28b6e15cad9c4af","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}