clockworklabs/SpacetimeDB · critical
scheduled table {table_id} doesn't have valid columns
Error message
scheduled table {table_id} doesn't have valid columns What it means
During SchedulerActor initialization the host iterates every row of the st_scheduled system table and asks the database for each table's scheduled id/at columns via table_scheduled_id_and_at. If that lookup returns None — the table registered as scheduled does not carry the injected scheduled_id/scheduled_at columns — initialization aborts with this error. Scheduled tables get those columns from the SDK's scheduled-table macro, so a None result means the on-disk schema does not look like a properly compiled scheduled table.
Source
Thrown at crates/core/src/host/scheduler.rs:111
// Draining rx before processing schedules from the DB to ensure there are no in-flight messages,
// as this can result in duplication.
//
// Explanation: By this time, if the `Scheduler::schedule` method has been called (the `init` reducer can do that),
// there will be an in-flight message in tx that has already been inserted into the DB.
// We are building the `queue` below with the DB and then spawning `SchedulerActor`, which will processes
// the in-flight message, resulting in a duplicate entry in the queue.
while self.rx.try_recv().is_ok() {}
// Find all Scheduled tables
for st_scheduled_row in self.db.iter(&tx, ST_SCHEDULED_ID)? {
let table_id = st_scheduled_row.read_col(StScheduledFields::TableId)?;
let function_name =
Arc::<str>::from(st_scheduled_row.read_col::<Box<str>>(StScheduledFields::ReducerName)?);
let (id_column, at_column) = self
.db
.table_scheduled_id_and_at(&tx, table_id)?
.ok_or_else(|| anyhow!("scheduled table {table_id} doesn't have valid columns"))?;
let now_ts = Timestamp::now();
let now_instant = Instant::now();
// Insert each entry (row) in the scheduled table into `queue`.
for scheduled_row in self.db.iter(&tx, table_id)? {
let (schedule_id, schedule_at) = get_schedule_from_row(&scheduled_row, id_column, at_column)?;
// calculate duration left to call the scheduled reducer
let duration = schedule_at.to_duration_from(now_ts);
let at = schedule_at.to_timestamp_from(now_ts);
let id = ScheduledFunctionId {
table_id,
schedule_id,
id_column,
at_column,
};
let key = queue.insert_at(
QueueItem::Id {View on GitHub (pinned to 524b4487d9)
Solutions
- Republish the module with an SDK version matched to the host so scheduled tables are (re)created with the id/at columns.
- If stale st_scheduled rows from an older module persist, clear them or recreate the database, then publish again.
- Verify via schema introspection that every scheduled table exposes the generated scheduled_id and scheduled_at columns.
- If data must be preserved, write a one-off migration that adds the missing columns before the scheduler starts.
Example fix
// before: table marked scheduled but declared without the SDK macro (no id/at columns)
#[spacetimedb::table]
pub struct Job { /* ... */ }
// after: use the scheduled-table macro so the id/at columns are generated
#[spacetimedb::table(scheduled)]
pub struct Job { /* ... */ } Defensive patterns
Strategy: validation
Validate before calling
// Before publishing, assert every scheduled table carries the macro-generated columns
for table in generated_schema.tables() {
if table.is_scheduled {
assert!(table.has_col("scheduled_id") && table.has_col("scheduled_at"),
"scheduled table {} lacks generated columns", table.name);
}
} Prevention
- Declare scheduled tables only through the SDK's scheduled-table macro.
- Never edit st_* system tables manually.
- Keep module SDK and host versions in lockstep across upgrades.
- Test publishes on a copy of the database after version upgrades.
When it happens
Trigger: A database where a table is marked scheduled in st_scheduled but its schema lacks the schedule id/at columns: modules compiled by an outdated or mismatched SDK, hand-crafted schemas that skipped the macro-generated columns, or leftover system-table state after an incompatible module update.
Common situations: Upgrading host or SDK across versions where scheduled-column generation changed; publishing a module built with a different SDK than the one that created the database; restoring a database from a backup made by an older build.
Related errors
- unknown reducerId ${reducerId}
- unknown viewId ${id}
- unknown anonViewId ${id}
- unknown procedureId ${id}
- Response data is missing.
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/16e35f4a0496e090.
Report an issue: GitHub.