{"record":{"id":"710aa46beea8ae03","repo":"Hmbown/CodeWhale","slug":"event-transaction-runs-once","errorCode":null,"errorMessage":"event transaction runs once","messagePattern":"event transaction runs once","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":1855,"sourceCode":"            use std::os::unix::fs::PermissionsExt as _;\n            file.set_permissions(fs::Permissions::from_mode(0o600))\n                .context(\"Failed to secure Runtime event lock\")?;\n        }\n        Ok(file)\n    }\n\n    fn with_event_transaction<T>(\n        &self,\n        timeout: Duration,\n        operation: impl FnOnce() -> Result<T>,\n    ) -> Result<T> {\n        let mut lock = fd_lock::RwLock::new(self.open_event_lock()?);\n        let started = Instant::now();\n        let mut operation = Some(operation);\n        loop {\n            match lock\n                .try_write()\n                .map(|_guard| operation.take().expect(\"event transaction runs once\")())\n            {\n                Ok(result) => return result,\n                Err(error)\n                    if matches!(\n                        error.kind(),\n                        std::io::ErrorKind::WouldBlock | std::io::ErrorKind::Interrupted\n                    ) =>\n                {\n                    wait_for_event_lock(started, timeout)?;\n                }\n                Err(error) => return Err(error).context(\"Failed to lock Runtime events\"),\n            }\n        }\n    }\n\n    fn record_path(base: &Path, id: &str, extension: &str, label: &str) -> Result<PathBuf> {\n        let id = validated_record_id(id, label)?;\n        Ok(base.join(format!(\"{id}.{extension}\")))","sourceCodeStart":1837,"sourceCodeEnd":1873,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_threads.rs#L1837-L1873","documentation":"Panic inside the event-file transaction retry loop: `operation.take().expect(\"event transaction runs once\")` asserts the closure is only consumed once per loop iteration. Because `operation` is reset to `Some(operation)` each iteration before `try_write`, the expect should be unreachable; it panics only if the closure was already taken (a control-flow bug where the loop body could execute twice on one assignment).","triggerScenarios":"A future refactor of the retry loop that calls the closure more than once, or `try_write` succeeding spuriously after `operation` was consumed — i.e., the loop body running without re-arming `operation`.","commonSituations":"Code review/refactor of the fd_lock retry logic introducing a second `take()` call or missing the reset of `operation` at the top of the loop.","solutions":["Keep the `operation = Some(operation)` re-assignment at the top of each loop iteration intact","Restructure to pass the closure by value into the match arm instead of Option::take to make double-consumption impossible","If the panic fires, audit recent changes to the retry loop for a second take() path"],"exampleFix":"// before\nlet mut operation = Some(operation);\nloop {\n    match lock.try_write().map(|_g| operation.take().expect(\"event transaction runs once\")()) {\n// after\nloop {\n    match lock.try_write() {\n        Ok(_guard) => return operation(),\n        Err(error) if would_block_or_interrupted => { /* retry */ }\n    }","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"if let Some(run) = operation.take() { ... } // never unwrap/take without re-arming","tryCatchPattern":"// Avoid Option::take; move the closure into the arm so double-use is a compile error\nmatch lock.try_write() { Ok(_) => return operation(), Err(e) => handle(e) }","preventionTips":["Never consume the closure via take() without resetting it in the same iteration","Add a loop-invariant comment/test that operation is Some at loop top","Restructure to pass the closure by reference or re-create it per attempt"],"tags":["rust","panic","invariant","file-locking","retry-loop"],"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-22T11:17:16.035Z"}