clockworklabs/SpacetimeDB · error · syn::Error

scheduled tables must have a `scheduled_at: spacetimedb::Sch

Error message

scheduled tables must have a `scheduled_at: spacetimedb::ScheduleAt` column. if the column has a name besides `scheduled_at`, you can specify it with `scheduled(my_reducer, at = custom_scheduled_at)`

What it means

Scheduled-table validation found a primary-key column but no time column: there is no field named `scheduled_at` and no `at = custom` option resolving to one. The error, emitted at the `scheduled` argument's span, suggests either adding the standard column or pointing `at = ...` at a custom-named `ScheduleAt` column.

Source

Thrown at crates/bindings-macro/src/table.rs:1096

        .as_ref()
        .map(|sched| {
            let scheduled_at_column = match &sched.at {
                Some(at) => Some(find_column(&columns, at)?),
                None => try_find_column(&columns, "scheduled_at"),
            };
            // better error message when both are missing
            if scheduled_at_column.is_none() && primary_key_column.is_none() {
                return Err(syn::Error::new(
                    sched.span,
                    "scheduled table missing required columns; add these to your struct:\n\
                             #[primary_key]\n\
                             #[auto_inc]\n\
                             scheduled_id: u64,\n\
                             scheduled_at: spacetimedb::ScheduleAt,",
                ));
            }
            let scheduled_at_column = scheduled_at_column.ok_or_else(|| {
                syn::Error::new(
                    sched.span,
                    "scheduled tables must have a `scheduled_at: spacetimedb::ScheduleAt` column. \
                             if the column has a name besides `scheduled_at`, you can specify it with \
                             `scheduled(my_reducer, at = custom_scheduled_at)`",
                )
            })?;
            let primary_key_column = primary_key_column.ok_or_else(|| {
                syn::Error::new(
                    sched.span,
                    "scheduled tables must have a `#[primary_key] #[auto_inc] scheduled_id: u64` column",
                )
            })?;

            let reducer_or_procedure = &sched.reducer_or_procedure;
            let scheduled_at_id = scheduled_at_column.index;
            let desc = quote!(spacetimedb::table::ScheduleDesc {
                reducer_or_procedure_name: <#reducer_or_procedure as spacetimedb::rt::FnInfo>::NAME,
                scheduled_at_column: #scheduled_at_id,

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Add a `scheduled_at: spacetimedb::ScheduleAt` column
  2. Or, for a custom column name, declare it with `scheduled(reducer, at = your_column)`
  3. If the column was removed by mistake, restore it

Example fix

// before
#[spacetimedb::table(accessor = jobs, scheduled(run_jobs))]
struct Jobs {
    #[primary_key]
    #[auto_inc]
    scheduled_id: u64,
    fire_at: spacetimedb::ScheduleAt,
}

// after
#[spacetimedb::table(accessor = jobs, scheduled(run_jobs, at = fire_at))]
struct Jobs {
    #[primary_key]
    #[auto_inc]
    scheduled_id: u64,
    fire_at: spacetimedb::ScheduleAt,
}
Defensive patterns

Strategy: validation

Validate before calling

# scheduled tables missing the time column
grep -rl 'scheduled(' src/ | xargs -r grep -L 'scheduled_at' || echo OK

Prevention

When it happens

Trigger: A scheduled table where a primary-key column exists but no `scheduled_at` field exists and no `at = ...` reference resolves to a field.

Common situations: Renaming `scheduled_at` (for example to `fire_at`) without adding `at = fire_at`; removing the time column while refactoring the scheduler schema.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/810e84a3fe3e5db9. Report an issue: GitHub.