{"record":{"id":"d2b8dc1b3c0eaeb1","repo":"clockworklabs/SpacetimeDB","slug":"unable-to-lock-database-for-update","errorCode":null,"errorMessage":"unable to lock database {} for update","messagePattern":"unable to lock database (.+?) for update","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/core/src/host/host_controller.rs","lineNumber":567,"sourceCode":"    pub async fn update_module_host(\n        &self,\n        database: Database,\n        host_type: HostType,\n        replica_id: u64,\n        program_bytes: Box<[u8]>,\n        policy: MigrationPolicy,\n    ) -> anyhow::Result<UpdateDatabaseResult> {\n        let program = Program::from_bytes(host_type.into(), program_bytes);\n        trace!(\n            \"update module host {}/{}: genesis={} update-to={}\",\n            database.database_identity,\n            replica_id,\n            database.initial_program,\n            program.hash\n        );\n\n        let Ok(mut guard) = self.acquire_write_lock(replica_id).await else {\n            bail!(\"unable to lock database {} for update\", database.database_identity);\n        };\n\n        // `HostController::clone` is fast,\n        // as all of its fields are either `Copy` or wrapped in `Arc`.\n        let this = self.clone();\n        let database_identity = database.database_identity;\n\n        // `try_init_host` is not cancel safe, as it will spawn other async tasks\n        // which hold a filesystem lock past when `try_init_host` returns or is cancelled.\n        // This means that, if `try_init_host` is cancelled, subsequent calls will fail.\n        //\n        // The rest of this future is also not cancel safe, as it will `Option::take` out of the guard\n        // at the start of the block and then store back into it at the end.\n        //\n        // This is problematic because Axum will cancel its handler tasks if the client disconnects,\n        // and this method is called from Axum handlers, e.g. for the publish route.\n        // `tokio::spawn` a task to update the contents of `guard`,\n        // so that it will run to completion even if the caller goes away.","sourceCodeStart":549,"sourceCodeEnd":585,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/fdd647dfac506d0967cc4492e9fce380706c94cb/crates/core/src/host/host_controller.rs#L549-L585","documentation":"update_database takes the per-replica write lock before swapping in a new program version. Acquisition is capped by a 5-second timeout; if a concurrent operation (running reducers, another update, host teardown) still holds the lock when the timeout elapses, the module update fails with this error naming the database identity.","triggerScenarios":"Publishing an update to a database whose replica write lock is held >5s by another operation -- e.g. an in-flight reducer call, a concurrent publish, or a previous update still initializing; or a stuck holder never releasing the lock.","commonSituations":"CI deploying while players are actively triggering reducers on a busy database; two deploy pipelines racing; retrying an update immediately after a cancelled one whose tasks still hold the lock.","solutions":["Retry the publish once the concurrent operation drains","Check server logs for the lock holder / deadlock symptoms (this timeout was introduced to debug exactly that)","Restart the node if the lock appears stuck with no live operation","Schedule updates during low traffic or gate them with a deploy lock"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"# shell: retry the update after the lock holder drains\nfor i in 1 2 3; do\n  spacetime publish my-db --project-path . && exit 0\n  echo \"attempt $i: replica lock busy during update\" >&2\n  sleep 10\ndone\nexit 1","preventionTips":["Deploy updates in a maintenance window or behind a deploy lock","Don't race two deploy pipelines against the same database","After cancelling a publish, give the holder a moment to release before retrying","Watch server logs for the lock holder when timeouts repeat"],"tags":["concurrency","locks","publish","update","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"}