clockworklabs/SpacetimeDB · error

ChangeColumnAccessorName: `{table_name}` not found in new mo

Error message

ChangeColumnAccessorName: `{table_name}` not found in new module def

What it means

Thrown while applying AutoMigrateStep::ChangeColumnAccessorName: the plan renames a column's accessor (the field alias used by module code) but plan.new.find_table((namespace, local)) fails, so the column list to search cannot even be obtained. The migration step's table key does not resolve in the new module definition the engine is applying against - plan and defs are inconsistent. The update aborts with no changes applied for this step.

Source

Thrown at crates/engine/src/update.rs:390

                    plan.new.find_table(table_name_key).ok_or_else(|| {
                        anyhow::anyhow!("ChangeTableAccessorName: `{table_name}` not found in new module def")
                    })?;

                let table_id = stdb.table_id_from_name_mut(tx, &table_name)?.unwrap();
                let new_alias = namespace.join(new_table_def.accessor_name.clone());

                log!(
                    logger,
                    "Changing table accessor name for `{table_name}` to `{new_alias}`",
                );
                stdb.alter_table_accessor_name(tx, table_id, new_alias)?;
            }
            spacetimedb_schema::auto_migrate::AutoMigrateStep::ChangeColumnAccessorName(table_name_key, col_name) => {
                let (namespace, local) = table_name_key;
                let table_name = joined(namespace, local);
                let (_, new_table_def): (&ModuleDef, &spacetimedb_schema::def::TableDef) =
                    plan.new.find_table(table_name_key).ok_or_else(|| {
                        anyhow::anyhow!("ChangeColumnAccessorName: `{table_name}` not found in new module def")
                    })?;
                let new_col_def = new_table_def
                    .columns
                    .iter()
                    .find(|col| &col.name == col_name)
                    .ok_or_else(|| anyhow::anyhow!("Column `{col_name}` not found in table `{table_name}`"))?;

                let table_id = stdb.table_id_from_name_mut(tx, &table_name)?.unwrap();
                let table_schema = stdb.schema_for_table_mut(tx, table_id)?;
                let col_schema = table_schema
                    .columns
                    .iter()
                    .find(|col| &col.col_name == col_name)
                    .ok_or_else(|| anyhow::anyhow!("Column `{col_name}` not found in table `{table_name}`"))?;

                log!(
                    logger,
                    "Changing column accessor name for `{}`.`{}` to `{}`",

View on GitHub (pinned to 6dee26c6ef)

Solutions

  1. Clean-rebuild and republish: cargo clean -p <module-crate> (or remove target/), spacetime build, spacetime publish <db>.
  2. Split the publish: first change only the column accessor (table stored name and module path untouched), then apply the table rename/move in a second publish.
  3. Pin the table's stored name with #[spacetimedb::table(name = "...")] so the planner's (namespace, local) key stays stable across refactors.
  4. Make SDK, CLI, and server versions identical to those that published the live database.
  5. On dev databases: spacetime publish --delete-data <db> recreates the schema without auto-migration.
  6. Reproduces on clean builds? File a SpacetimeDB issue with the module diff - planner invariant violation.

Example fix

// before: one publish renames the column accessor AND moves the table
mod guild {
    #[spacetimedb::table(name = "members")]
    pub struct Member { pub life: u32 } // field was `hp`
}

// after: two publishes
// 1) accessor-only change, table untouched
#[spacetimedb::table(name = "players")]
pub struct Player { pub life: u32 } // was `hp`
// 2) then publish the table move separately
Defensive patterns

Strategy: validation

Validate before calling

use spacetimedb_schema::auto_migrate::AutoMigrateStep;
for step in &plan.steps {
    if let AutoMigrateStep::ChangeColumnAccessorName(key, _) = step {
        if plan.new.find_table(key).is_none() {
            anyhow::bail!("plan step references table {:?} missing from new module def", key);
        }
    }
}

Type guard

fn table_resolves_in_new_def(plan: &AutoMigratePlan, key: (&str, &str)) -> bool {
    plan.new.find_table(key).is_some()
}

Try / catch

match update_database(&stdb, tx, &plan, ...).await {
    Err(e) if e.to_string().contains("ChangeColumnAccessorName")
        && e.to_string().contains("not found in new module def") => {
        // Split the publish: accessor rename first, table rename/move later.
    }
    result => result?,
}

Prevention

When it happens

Trigger: Publishing a build where a column accessor changed while its table was also renamed, moved to a submodule, or removed, so the table key no longer matches; or the new module def differs from what the planner diffed (stale target/ artifacts, mismatched compiler/SDK versions).

Common situations: Field renames bundled with table refactors; teams publishing the same database from machines with different spacetimedb toolchain versions; leftover target/ directories after toolchain upgrades.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20). Data as JSON: /api/errors/96e4ac096f3de999. Report an issue: GitHub.