{"record":{"id":"e1965c6b5706be29","repo":"clockworklabs/SpacetimeDB","slug":"table-tablename-defines-a-schedule-but-it-seem","errorCode":null,"errorMessage":"Table ${tableName} defines a schedule, but it seems like the associated function was not exported.","messagePattern":"Table (.+?) defines a schedule, but it seems like the associated function was not exported\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/bindings-typescript/src/server/schema.ts","lineNumber":190,"sourceCode":"          'Schedule target table is not part of this schema.'\n        );\n      }\n\n      const scheduleAtCol = knownScheduleAtCol ?? table.scheduleAtCol;\n      if (scheduleAtCol === undefined) {\n        throw new TypeError(\n          `Table ${tableName} defines a schedule, but it does not have a ScheduleAt column.`\n        );\n      }\n\n      const functionName =\n        knownFunctionName ??\n        (reducer === undefined\n          ? undefined\n          : this.functionExports.get(reducer()));\n      if (functionName === undefined) {\n        const msg = `Table ${tableName} defines a schedule, but it seems like the associated function was not exported.`;\n        throw new TypeError(msg);\n      }\n      const existingFunctionName = scheduledTables.get(tableName);\n      if (existingFunctionName !== undefined) {\n        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() {","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/eddf9f5014579a50d4b67630e28b6e15cad9c4af/crates/bindings-typescript/src/server/schema.ts#L172-L208","documentation":"A schedule needs the function that will run when rows mature. Resolution looks the reducer/procedure up via `this.functionExports.get(reducer())`; if the function referenced by the schedule was never exported from the module, `functionName` is undefined and the build fails with 'the associated function was not exported'. The scheduled function must be a module export registered in the same schema.","triggerScenarios":"Using the legacy `schedule(fn)` table API where `fn` is not exported; defining the scheduled reducer but forgetting `export const ...`; the schedule referencing a function object different from the one actually exported (two closures created by a factory).","commonSituations":"Factory-created reducers (`const make = () => spacetimedb.reducer(...)`): the factory is called twice — once for the export, once for the schedule — so identity lookup fails; refactors that remove an export but leave a legacy schedule pointing at it.","solutions":["Export the scheduled function from the module: `export const processMessage = spacetimedb.reducer({ onSchedule: scheduledMessages }, ...)`.","With the modern `onSchedule` API the function and schedule are declared together — prefer it over the legacy `schedule()` API to avoid identity mismatches.","If using a factory, call it once, store the result, and use that same object for both the export and the schedule."],"exampleFix":"// before (legacy style)\nconst processMessage = () => spacetimedb.reducer(...);\nconst messages = table({ schedule: scheduleRef }, row); // fn never exported\n\n// after\nexport const processMessage = spacetimedb.reducer(\n  { onSchedule: scheduledMessages },\n  { scheduledMessage: scheduledMessages.rowType },\n  (ctx, { scheduledMessage }) => {}\n);","handlingStrategy":"validation","validationCode":"// With factories, register the exact object you export.\nconst registeredFns = new Set<Function>();\nfunction exportOnce<T extends Function>(name: string, fn: T): T {\n  if (registeredFns.has(fn)) throw new Error(`${name} must be exported exactly once`);\n  registeredFns.add(fn);\n  return fn;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer the modern `onSchedule` API, which declares function and table together.","Always `export const` the scheduled function in the same file where it is defined.","Call reducer factories exactly once and reuse the returned export object."],"tags":["scheduling","exports","reducer","build-time","spacetimedb"],"backgroundTag":"unexported-schedule-reducer","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"}