{"record":{"id":"75c9fc12db205ae5","repo":"Hmbown/CodeWhale","slug":"err-approval-log","errorCode":null,"errorMessage":"err","messagePattern":"err","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/approval_log.rs","lineNumber":239,"sourceCode":"        let path = self.log_path(session_id)?;\n        let file = match File::open(path) {\n            Ok(file) => file,\n            Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(Vec::new()),\n            Err(err) => return Err(err),\n        };\n        let mut receipts = Vec::new();\n        for (index, line) in BufReader::new(file).lines().enumerate() {\n            let line = line?;\n            let receipt = serde_json::from_str(&line).map_err(|err| {\n                io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\"invalid approval receipt at line {}: {err}\", index + 1),\n                )\n            })?;\n            receipts.push(receipt);\n        }\n        ApprovalReplay::from_receipts(&receipts)\n            .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;\n        Ok(receipts)\n    }\n\n    pub(crate) fn load(&self, session_id: &str) -> io::Result<Vec<ApprovalReceipt>> {\n        if !self.log_path(session_id)?.exists() {\n            return Ok(Vec::new());\n        }\n        let Some(lock_file) = self.open_existing_lock_file(session_id)? else {\n            // Imported or legacy snapshots can contain a receipt log without\n            // its ephemeral lock file. Preserve read-only session loading;\n            // live writers always publish the lock before creating the log.\n            return self.load_unlocked(session_id);\n        };\n        let lock = fd_lock::RwLock::new(lock_file);\n        let _guard = lock.read()?;\n        self.load_unlocked(session_id)\n    }\n","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/approval_log.rs#L221-L257","documentation":"load_unlocked parses all receipt lines and then rebuilds session state via ApprovalReplay::from_receipts. If the receipts are individually valid JSON but their sequence is semantically inconsistent for replay (e.g. a state machine transition that cannot happen), the replay error is wrapped in an io::Error with InvalidData. This is a semantic-validation failure of the approval history, not a parse failure.","triggerScenarios":"load/append call load_unlocked and the parsed receipt sequence violates ApprovalReplay's invariants — out-of-order receipts, a decision referencing an unknown request, or duplicate/conflicting receipts.","commonSituations":"Log assembled from merged sessions or copied between machines; concurrent writers bypassing the lock file; receipts from a version with different replay rules.","solutions":["Inspect the log's receipt order and remove/reorder the offending line identified by replay's error text","Delete the log to reset replay state if the history is not needed","Verify all writers go through ApprovalLog::append (which takes the fd_lock write lock) rather than appending directly","Check for version skew: replay rules may have changed between crate versions"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let receipts: Vec<ApprovalReceipt> = log.load(session_id)?;\nApprovalReplay::from_receipts(&receipts)?; // surface semantic issues before use","typeGuard":null,"tryCatchPattern":"let replay = log.replay(session_id)\n    .map_err(|e| e.context(\"approval history failed replay validation\"))?;","preventionTips":["Only append receipts through ApprovalLog::append (it holds the lock and pre-validates)","Keep receipt generation and replay logic versioned together","Never merge logs across sessions"],"tags":["rust","state-machine","validation","file-io"],"backgroundTag":"schema-validation-failed","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}