{"record":{"id":"94aca232dcd8dc63","repo":"Hmbown/CodeWhale","slug":"fleet-ledger-lock-was-replaced-reopen-the-workspace-before","errorCode":null,"errorMessage":"Fleet ledger lock was replaced; reopen the workspace before continuing","messagePattern":"Fleet ledger lock was replaced; reopen the workspace before continuing","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/fleet/ledger.rs","lineNumber":403,"sourceCode":"    #[cfg(test)]\n    pub(crate) fn fail_next_start_append_after_callback(&self) {\n        self.fail_start_append_after_callback\n            .store(true, std::sync::atomic::Ordering::SeqCst);\n    }\n\n    #[cfg(test)]\n    pub(crate) fn fail_next_restart_append_after_callback(&self) {\n        self.fail_restart_append_after_callback\n            .store(true, std::sync::atomic::Ordering::SeqCst);\n    }\n\n    fn open_lock_file(&self) -> Result<std::fs::File> {\n        let file = self\n            .lock_file\n            .open_update(false, false)\n            .with_context(|| format!(\"opening fleet ledger lock {}\", self.lock_path.display()))?;\n        if !same_file(&file, &self.original_lock)? {\n            bail!(\"Fleet ledger lock was replaced; reopen the workspace before continuing\");\n        }\n        Ok(file)\n    }\n\n    fn with_read_lock<T>(&self, action: impl FnOnce() -> Result<T>) -> Result<T> {\n        let lock_file = self.open_lock_file()?;\n        let lock = fd_lock::RwLock::new(lock_file);\n        let _guard = lock\n            .read()\n            .with_context(|| format!(\"read-locking fleet ledger {}\", self.ledger_path.display()))?;\n        action()\n    }\n\n    fn with_write_lock<T>(&self, action: impl FnOnce() -> Result<T>) -> Result<T> {\n        let lock_file = self.open_lock_file()?;\n        let mut lock = fd_lock::RwLock::new(lock_file);\n        let _guard = lock.write().with_context(|| {\n            format!(\"write-locking fleet ledger {}\", self.ledger_path.display())","sourceCodeStart":385,"sourceCodeEnd":421,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/fleet/ledger.rs#L385-L421","documentation":"The Fleet ledger keeps an open handle to its advisory lock file. open_lock_file re-opens the lock and compares it (via fs::same_file) to the handle captured at workspace open; if the inode differs, the lock file was deleted and recreated (or replaced) on disk, so continuing under the old handle would guard nothing. The ledger refuses to proceed and asks the caller to reopen the workspace.","triggerScenarios":"Any ledger operation that takes a read or write lock after the ledger's lock file at lock_path was unlinked and recreated — e.g. an external process deleted the lock file, a cleanup job pruned it, or the workspace directory was rebuilt while the ledger was open.","commonSituations":"A tmp-cleaning daemon or `make clean`-style script removed lock files; the workspace was moved/re-synced (rsync, container restart) while a process still held it; two tooling generations use different lock-file lifecycles in the same directory.","solutions":["Close the current FleetLedger/workspace handle and reopen the workspace so a fresh lock file and handle are captured, then retry the operation.","Identify what deleted/recreated the lock file (cleaner daemon, competing tooling) and exclude the workspace lock path from it.","If the workspace directory was rebuilt, restart all processes using it so every holder shares the same lock inode.","Avoid manually deleting lock files under an active fleet run; stop the run first."],"exampleFix":"// before\nlet ledger = FleetLedger::open(&path)?;\n// ...someone deletes the lock file...\nledger.append(...)?; // fails\n// after\nlet ledger = FleetLedger::open(&path)?; // reopen after the lock file was replaced\nledger.append(...)?;","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match ledger.append(record) {\n    Err(e) if e.to_string().contains(\"lock was replaced\") => {\n        let ledger = FleetLedger::open(&workspace)?; // reopen and retry once\n        ledger.append(record)\n    }\n    other => other,\n}","preventionTips":["Never delete lock files while a fleet workspace is open; stop runs first.","Exclude workspace lock paths from cleaner daemons and cleanup scripts.","Restart all holders after the workspace directory is moved or rebuilt so they share one lock inode."],"tags":["fleet","file-locking","concurrency","stale-handle"],"backgroundTag":"internal-invariant-violation","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T06:17:15.046Z"}