{"record":{"id":"4b1b507657401476","repo":"zeroclaw-labs/zeroclaw","slug":"prev-hash-mismatch-at-line-sequence-expec","errorCode":null,"errorMessage":"prev_hash mismatch at line {} (sequence {}): expected {}, got {}","messagePattern":"prev_hash mismatch at line (.+?) \\(sequence (.+?)\\): expected (.+?), got (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/zeroclaw-runtime/src/security/audit.rs","lineNumber":483,"sourceCode":"        let line = line?;\n        if line.trim().is_empty() {\n            continue;\n        }\n        let entry: AuditEvent = serde_json::from_str(&line)?;\n\n        // Check sequence continuity\n        if entry.sequence != expected_sequence {\n            bail!(\n                \"sequence gap at line {}: expected {}, got {}\",\n                line_idx + 1,\n                expected_sequence,\n                entry.sequence\n            );\n        }\n\n        // Check prev_hash linkage\n        if entry.prev_hash != expected_prev_hash {\n            bail!(\n                \"prev_hash mismatch at line {} (sequence {}): expected {}, got {}\",\n                line_idx + 1,\n                entry.sequence,\n                expected_prev_hash,\n                entry.prev_hash\n            );\n        }\n\n        // Recompute and verify entry_hash\n        let recomputed = compute_entry_hash(&entry.prev_hash, &entry);\n        if entry.entry_hash != recomputed {\n            bail!(\n                \"entry_hash mismatch at line {} (sequence {}): expected {}, got {}\",\n                line_idx + 1,\n                entry.sequence,\n                recomputed,\n                entry.entry_hash\n            );","sourceCodeStart":465,"sourceCodeEnd":501,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/security/audit.rs#L465-L501","documentation":"An entry's prev_hash does not equal the previous entry's entry_hash, so the hash-chain linkage is broken at that line. The chain proves each record hash-seals its predecessor; a mismatch means lines were inserted, removed, or reordered after the fact — the log has been spliced or corrupted.","triggerScenarios":"A forged line inserted into the middle of the log; two log segments from different runs concatenated out of order; lines deleted and neighbors not resealing (they never re-seal, hence detection); manual reorder during 'cleanup'.","commonSituations":"Tamper-evidence tests; merging audit logs from two daemon instances by accident; restoring an interleaved backup; text-editor saves that reorder or drop lines.","solutions":["Compare against the newest intact backup and restore it; quantify what changed since.","Treat as a tamper event: quarantine the log, alert security, preserve the original file.","If the cause is benign (merging logs), split back into the original per-daemon files and verify each separately.","Prevent recurrence: single writer via endpoint lock, append-only permissions where possible."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if let Err(e) = audit.verify_chain() {\n    if e.to_string().contains(\"prev_hash mismatch\") {\n        // splice/reorder detected: quarantine file, compare with last known-good backup, alert\n    }\n}","preventionTips":["Never merge or reorder audit log files; keep per-daemon logs separate.","Automate verification right after any backup/restore exercise.","Restrict write access to the audit log to the daemon account only."],"tags":["audit","integrity","hash-chain","tamper-detection","verification"],"backgroundTag":"hash-chain-mismatch","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}