{"record":{"id":"2baf2d53ac620a67","repo":"clockworklabs/SpacetimeDB","slug":"table-tablename-defines-multiple-schedules-e","errorCode":null,"errorMessage":"Table ${tableName} defines multiple schedules: ${existingFunctionName} and ${functionName}. A schedule table can only be used by one reducer or procedure.","messagePattern":"Table (.+?) defines multiple schedules: (.+?) and (.+?)\\. A schedule table can only be used by one reducer or procedure\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/bindings-typescript/src/server/schema.ts","lineNumber":194,"sourceCode":"      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() {\n    for (const route of this.pendingHttpRoutes) {\n      const handlerFunction = this.httpHandlerExports.get(route.handler);\n      if (handlerFunction === undefined) {\n        throw new TypeError(","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/eddf9f5014579a50d4b67630e28b6e15cad9c4af/crates/bindings-typescript/src/server/schema.ts#L176-L212","documentation":"The scheduler dispatches one function per schedule table, so `resolveSchedules` keeps a `scheduledTables` map of tableName -> functionName and throws if a second function claims a table already scheduled. The message names both the existing and the new function. One schedule table == one reducer or procedure, enforced at build time.","triggerScenarios":"Two reducers (or a reducer plus a procedure) both using `{ onSchedule: sameTable }`; registering a second scheduled function while iterating a list of tables with a shared handle; adding a new scheduled consumer for an existing schedule table.","commonSituations":"Splitting a busy scheduled handler into two functions and pointing both at the same table; library code that schedules on 'its' table which the app also schedules on; misunderstanding that the table, not the row type, identifies the schedule.","solutions":["Give the second function its own schedule table (a fresh `table()` handle with its own `scheduleAt` column) and insert into the table matching the desired behavior.","Alternatively, merge both handlers into one scheduled function that switches on the row's content.","Delete the duplicate registration named in the error message if it was left over from a refactor."],"exampleFix":"// before — two functions, one table\nexport const f1 = spacetimedb.reducer({ onSchedule: tasks }, ...);\nexport const f2 = spacetimedb.reducer({ onSchedule: tasks }, ...); // TypeError\n\n// after\nexport const f1 = spacetimedb.reducer({ onSchedule: urgentTasks }, ...);\nexport const f2 = spacetimedb.reducer({ onSchedule: routineTasks }, ...); // separate tables","handlingStrategy":"validation","validationCode":"// Enforce one scheduled function per table in dev tooling.\nconst scheduleOwners = new Map<object, string>();\nfunction claimScheduleTable(table: object, fnName: string) {\n  const prev = scheduleOwners.get(table);\n  if (prev !== undefined) throw new Error(`${table} already scheduled by ${prev}; ${fnName} needs its own table`);\n  scheduleOwners.set(table, fnName);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Create a fresh `table()` handle (with `t.scheduleAt()`) for every scheduled function.","If behaviors must share a queue, use one table and one function that dispatches on row content.","Document each schedule table's owner next to its definition."],"tags":["scheduling","schema","reducer","build-time","spacetimedb"],"backgroundTag":"multiple-schedules-per-table","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"}