{"record":{"id":"04de59dd5e0defaa","repo":"clockworklabs/SpacetimeDB","slug":"http-route-for-path-route-path-refers-to-a-ha","errorCode":null,"errorMessage":"HTTP route for path '${route.path}' refers to a handler that was not exported.","messagePattern":"HTTP route for path '(.+?)' refers to a handler that was not exported\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/bindings-typescript/src/server/schema.ts","lineNumber":212,"sourceCode":"        throw new TypeError(\n          `Table ${tableName} defines multiple schedules: ${existingFunctionName} and ${functionName}. A schedule table can only be used by one reducer or procedure.`\n        );\n      }\n      scheduledTables.set(tableName, functionName);\n      this.moduleDef.schedules.push({\n        sourceName: undefined,\n        tableName,\n        scheduleAtCol,\n        functionName,\n      });\n    }\n  }\n\n  resolveHttpRoutes() {\n    for (const route of this.pendingHttpRoutes) {\n      const handlerFunction = this.httpHandlerExports.get(route.handler);\n      if (handlerFunction === undefined) {\n        throw new TypeError(\n          `HTTP route for path '${route.path}' refers to a handler that was not exported.`\n        );\n      }\n      this.moduleDef.httpRoutes.push({\n        handlerFunction,\n        method: route.method,\n        path: route.path,\n      });\n    }\n  }\n}\n\ntype PendingSchedule = {\n  table: UntypedTableSchema;\n  tableName?: string;\n  scheduleAtCol?: number;\n  reducer?: () => UntypedScheduledFunctionExport;\n  functionName?: string;","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/eddf9f5014579a50d4b67630e28b6e15cad9c4af/crates/bindings-typescript/src/server/schema.ts#L194-L230","documentation":"When `resolveHttpRoutes` runs, every route registered on a Router must point at a handler object that was registered as an HTTP handler export — the lookup is `httpHandlerExports.get(route.handler)`, which is only populated when the handler's `[registerExport]` runs during module export registration. If you passed the raw function, a handler built by a different (or no) schema registration, or never exported the handler, the route cannot be resolved and the build fails with 'refers to a handler that was not exported'.","triggerScenarios":"Passing the inner function instead of the export object to `router.post(path, fn)`; creating a handler with `spacetimedb.httpHandler(...)` but not including it in the module's `export`s; passing a handler created lazily/inside a function so registration never happened when routes resolved.","commonSituations":"Refactoring a handler into a helper that returns the fn, then wiring the fn into the router; forgetting `export const` on the handler; handlers created in a loop where the export object is discarded.","solutions":["Pass the exact object returned by `spacetimedb.httpHandler(...)` to the router methods, and export that same object from the module: `export const listUsers = spacetimedb.httpHandler(...); router.get('/users', listUsers)`.","Do not wrap the handler in another function or pass `handler[httpHandlerFn]`-style internals.","Check that the export name in the module matches a real `export` statement; registration only happens for exported names (the `default` export name is skipped)."],"exampleFix":"// before\nfunction listUsers(ctx) { ... }\nexport default new Router().get('/users', listUsers); // raw fn, not an export\n\n// after\nexport const listUsers = spacetimedb.httpHandler(schema, undefined, ctx => { ... });\nexport default new Router().get('/users', listUsers);","handlingStrategy":"validation","validationCode":"// Verify every router route points at a real handler export before publishing.\nconst handlerExports = new Set<unknown>([listUsers, getUser]);\nfunction assertRouterHandlers(router: Router) {\n  for (const route of router.intoRoutes()) {\n    if (!handlerExports.has(route.handler)) {\n      throw new Error(`route ${route.path} uses an unexported handler`);\n    }\n  }\n}","typeGuard":"function isHttpHandlerExport(x: unknown, schemaExports: Set<unknown>): x is Function {\n  return typeof x === 'function' && schemaExports.has(x);\n}","tryCatchPattern":null,"preventionTips":["Always pass the object returned by `spacetimedb.httpHandler(...)` to router methods — never the bare function.","`export const` every handler in the module entry; check the list before publish.","Remember the `default` export name is skipped by export registration — handlers must be named exports."],"tags":["http","routing","exports","build-time","spacetimedb"],"backgroundTag":"handler-not-exported","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"}