clockworklabs/SpacetimeDB · error · syn::Error

scheduled table missing required columns; add these to your

Error message

scheduled table missing required columns; add these to your struct:
#[primary_key]
#[auto_inc]
scheduled_id: u64,
scheduled_at: spacetimedb::ScheduleAt,

What it means

A table marked `scheduled(...)` must contain both a `#[primary_key] #[auto_inc] scheduled_id: u64` column and a `scheduled_at: spacetimedb::ScheduleAt` column; the scheduler stores its id and firing time in them. When validation finds neither a primary-key column nor any scheduled_at column, this combined guidance is emitted at the `scheduled` argument's span, listing the exact lines to add.

Source

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

    }).collect();

    let default_fn: TokenStream = quote! {
        fn get_default_col_values() -> Vec<spacetimedb::table::ColumnDefault> {
            [#(#col_defaults)*].to_vec()
        }
    };

    let (schedule, schedule_typecheck) = args
        .scheduled
        .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(

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Add the two required columns exactly as the message shows: `#[primary_key] #[auto_inc] scheduled_id: u64` and `scheduled_at: spacetimedb::ScheduleAt`
  2. Keep the conventional names unless you also pass `at = ...` for the time column
  3. Re-run cargo check to confirm no further scheduled-table errors follow

Example fix

// before
#[spacetimedb::table(accessor = ticks, scheduled(run_ticks))]
struct Ticks {
    payload: String,
}

// after
#[spacetimedb::table(accessor = ticks, scheduled(run_ticks))]
struct Ticks {
    #[primary_key]
    #[auto_inc]
    scheduled_id: u64,
    scheduled_at: spacetimedb::ScheduleAt,
    payload: String,
}
Defensive patterns

Strategy: validation

Validate before calling

# every scheduled table needs both standard columns
grep -rl 'scheduled(' src/ | xargs -r grep -L 'scheduled_id'
grep -rl 'scheduled(' src/ | xargs -r grep -L 'scheduled_at'

Prevention

When it happens

Trigger: `#[spacetimedb::table(accessor = t, scheduled(reducer))]` on a struct whose fields include neither a `#[primary_key]` column nor a `scheduled_at` (or `at = ...`) column.

Common situations: Writing a first scheduled table without starting from the template; deleting or renaming both standard columns while customizing the schema.

Related errors


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