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
- Read the 'reason' lines printed immediately above the error -- they name the exact change that forces manual migration.
- Adjust the schema so the diff is auto-migratable (additive changes, compatible type widening) and retry.
- If data loss is acceptable (dev/staging), re-run with `--delete-data=on-conflict` (clear only on conflict) or plain `--delete-data` (always clear).
- 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
- Stage schema changes additively (add before remove) so the auto-migrator never hits ManualMigrate.
- For disposable dev databases, standardize on --delete-data=on-conflict in your dev scripts.
- Never put --delete-data in production pipelines; treat this abort as the guardrail doing its job.
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
- AddColumns: table ` ` not found in new module def
- AddConstraint: ` ` not found in new module def
- AddRowLevelSecurity: RLS
- AddSequence: ` ` not found in new module def
- ChangeAccess: ` ` not found as a table or view in new…
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)