{"record":{"id":"98cd2da4f49a1be6","repo":"clockworklabs/SpacetimeDB","slug":"unable-to-lock-database-for-migration-planning","errorCode":null,"errorMessage":"unable to lock database {} for migration planning","messagePattern":"unable to lock database (.+?) for migration planning","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/core/src/host/host_controller.rs","lineNumber":638,"sourceCode":"    pub async fn migrate_plan(\n        &self,\n        database: Database,\n        host_type: HostType,\n        replica_id: u64,\n        program_bytes: Box<[u8]>,\n        style: PrettyPrintStyle,\n    ) -> anyhow::Result<MigratePlanResult> {\n        let program = Program::from_bytes(host_type.into(), program_bytes);\n        trace!(\n            \"migrate plan {}/{}: genesis={} update-to={}\",\n            database.database_identity,\n            replica_id,\n            database.initial_program,\n            program.hash\n        );\n\n        let Ok(guard) = self.acquire_read_lock(replica_id).await else {\n            bail!(\n                \"unable to lock database {} for migration planning\",\n                database.database_identity\n            );\n        };\n        let host = guard.as_ref().ok_or(NoSuchModule)?;\n\n        host.migrate_plan(\n            self.page_pool.clone(),\n            self.bsatn_rlb_pool.clone(),\n            &self.runtimes,\n            host_type,\n            program,\n            style,\n        )\n        .await\n    }\n\n    /// Release all resources of the [`ModuleHost`] identified by `replica_id`,","sourceCodeStart":620,"sourceCodeEnd":656,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/fdd647dfac506d0967cc4492e9fce380706c94cb/crates/core/src/host/host_controller.rs#L620-L656","documentation":"Planning a migration (the migrate-plan path that pretty-prints schema changes) first takes a per-replica read lock on the module host. Read-lock acquisition also has a 5-second timeout; when a writer (an in-progress publish/update or host initialization) holds the lock past that window, planning fails with this error naming the database identity.","triggerScenarios":"Running `spacetime migrate plan` (or anything invoking migrate_plan) while a publish/update of the same database holds the write lock for more than 5 seconds; or a stuck holder blocking all lockers.","commonSituations":"Planning a migration while a CI deploy is publishing; a long-running update initialization overlapping the plan request; retrying a plan right after cancelling an update whose tasks still hold the lock.","solutions":["Retry the migrate plan after the concurrent publish/update completes","Check server logs to identify the writer holding the replica lock","Restart the node if no operation is legitimately in flight yet the lock never frees","Sequence 'plan then apply' migrations in tooling so they don't race deploys"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"# shell: retry migration planning after concurrent writes finish\nfor i in 1 2 3; do\n  spacetime migrate plan my-db --project-path . && exit 0\n  echo \"attempt $i: replica lock busy during planning\" >&2\n  sleep 10\ndone\nexit 1","preventionTips":["Run migrate plan when no publish/update is in flight for the database","Sequence plan-then-apply in tooling so they never overlap deploys","Alert on planning failures in CI migrations","Remember planning needs only a read lock - a long write (update) is what blocks it"],"tags":["concurrency","locks","migration","planning","timeout"],"backgroundTag":"lock-acquisition-timeout","analyzedSha":"fdd647dfac506d0967cc4492e9fce380706c94cb","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}