clockworklabs/SpacetimeDB · error · anyhow::Error

Aborting because publishing would require manual migration…

Error message

Aborting because publishing would require manual migration or deletion of data and --delete-data was not specified.

What it means

Before uploading, the CLI calls the server's pre-publish check, which classifies the schema diff. PrePublishResult::ManualMigrate means the changes cannot be applied automatically (the printed manual.reason lines name the offending change). Because the clear-database mode is ClearMode::Never (the default when --delete-data is absent), the CLI aborts rather than destroy or hand-migrate data.

Solutions

  1. Read the 'reason' lines printed immediately above the error -- they name the exact change that forces manual migration.
  2. Adjust the schema so the diff is auto-migratable (additive changes, compatible type widening) and retry.
  3. If data loss is acceptable (dev/staging), re-run with `--delete-data=on-conflict` (clear only on conflict) or plain `--delete-data` (always clear).
  4. For a throwaway dev database, `spacetime delete <db>` and publish fresh.

Example fix

# before
spacetime publish quickstart
# after (dev database, data may be cleared)
spacetime publish quickstart --delete-data=on-conflict
Defensive patterns

Strategy: try-catch

Validate before calling

# In dev/staging where clearing is acceptable, opt in up front:
spacetime publish mydb --delete-data=on-conflict
# In prod, keep the default and review the printed manual.reason lines instead

Try / catch

#!/usr/bin/env bash
spacetime publish mydb
status=$?
if [ $status -ne 0 ] && spacetime publish mydb 2>&1 | tail -20 | grep -q "requires manual migration"; then
  read -p "Schema conflict: clear database data? [y/N] " ans
  [ "$ans" = y ] && spacetime publish mydb --delete-data=on-conflict
fi

Prevention

When it happens

Trigger: `spacetime publish` to an existing database whose schema diff requires manual migration -- e.g. dropping or renaming tables, incompatible column type changes, altering indexed columns -- against a database with existing data, with no --delete-data or --delete-data=on-conflict flag.

Common situations: Iterating on module schemas during development; renaming/removing tables that hold rows; production publishes after schema rewrites; the exact blocking change is always printed on the lines just above this error (manual.reason).

Related errors


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

Appendix: source

Thrown at crates/cli/src/subcommands/publish.rs:936

        program_bytes,
        auth_header,
    )
    .await?
    {
        let major_version_upgrade = match &pre {
            PrePublishResult::AutoMigrate(auto) => auto.major_version_upgrade,
            PrePublishResult::ManualMigrate(manual) => manual.major_version_upgrade,
        };
        if major_version_upgrade {
            confirm_major_version_upgrade(yes.migrate_major_version)?;
        }

        match pre {
            PrePublishResult::ManualMigrate(manual) => {
                if clear_database == ClearMode::Never {
                    println!("{}", manual.reason);
                    println!("Aborting publish due to required manual migration.");
                    anyhow::bail!("Aborting because publishing would require manual migration or deletion of data and --delete-data was not specified.");
                }
                println!("{}", manual.reason);
                println!("Proceeding with database clear due to --delete-data=on-conflict.");

                builder = confirm_and_clear(name_or_identity, yes.delete_data, builder)?;
            }
            PrePublishResult::AutoMigrate(auto) => {
                println!("{}", auto.migrate_plan);
                // We only arrive here if you have not specified ClearMode::Always AND there was no
                // conflict that required manual migration.
                if auto.break_clients
                    && !y_or_n(
                        force_break_clients || yes.break_clients,
                        "The above changes will BREAK existing clients. Do you want to proceed?",
                    )?
                {
                    println!("Aborting");
                    // Early exit: return an error or a special signal. Here we bail out by returning Err.

View on GitHub (pinned to eddf9f5014)