{"record":{"id":"c5e8eb50eacaa456","repo":"BigPizzaV3/CodexPlusPlus","slug":"recovery-journal-conflicts-with-verified-content","errorCode":null,"errorMessage":"Recovery journal conflicts with verified content","messagePattern":"Recovery journal conflicts with verified content","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/native_browser.rs","lineNumber":479,"sourceCode":"fn recovery_material(\n    paths: &BrowserPaths,\n    key: &str,\n    contract: &RuntimeContract,\n) -> Result<(Journal, Vec<u8>, Vec<u8>)> {\n    ensure!(key_valid(key), \"Invalid recovery key\");\n    let dir = paths.state_root.join(key);\n    let journal: Journal = serde_json::from_slice(&read_regular(&dir.join(\"journal.json\"), 4096)?)?;\n    let original = read_regular(&dir.join(\"original.mjs\"), MAX_SERVICE)?;\n    ensure!(\n        journal.candidate_sha.len() == 64\n            && journal.candidate_sha.bytes().all(|b| b.is_ascii_hexdigit()),\n        \"Invalid candidate hash\"\n    );\n    let candidate = read_regular(\n        &dir.join(format!(\"candidate-{}.mjs\", journal.candidate_sha)),\n        MAX_SERVICE,\n    )?;\n    ensure!(\n        journal.schema == 1\n            && (journal.original_sha == contract.service_sha\n                || journal.original_sha == ORIGINAL_SHA)\n            && sha(&original) == journal.original_sha\n            && journal.candidate_sha == sha(&candidate)\n            && journal.modified_nanos < 1_000_000_000,\n        \"Recovery journal conflicts with verified content\"\n    );\n    Ok((journal, original, candidate))\n}\n\nfn restore_all(paths: &BrowserPaths, keep: Option<&str>, contract: &RuntimeContract) -> Result<()> {\n    let mut pending = Vec::new();\n    let mut guards = Vec::new();\n    for entry in fs::read_dir(&paths.state_root)? {\n        let entry = entry?;\n        let key = entry.file_name().to_string_lossy().to_string();\n        if !key_valid(&key) || keep == Some(key.as_str()) {","sourceCodeStart":461,"sourceCodeEnd":497,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/native_browser.rs#L461-L497","documentation":"`recovery_material` cross-checks the journal against verified on-disk content: schema must be 1, `original_sha` must equal the contract's service sha or the pinned `ORIGINAL_SHA`, `original.mjs` must hash to `original_sha`, the candidate file must hash to `candidate_sha`, and `modified_nanos` must be a valid nanosecond value. Any mismatch means the journal's claims are inconsistent with the actual backup files, so recovery would restore unverified bytes; Codex++ refuses.","triggerScenarios":"Journal/file divergence detected while loading recovery state: `original.mjs` edited after journaling, `candidate-<sha>.mjs` replaced or truncated, journal from an older schema or a different runtime version (original_sha neither contract sha nor ORIGINAL_SHA), or a hand-written `modified_nanos >= 1e9`.","commonSituations":"Plugin runtime upgraded so the pinned contract sha no longer matches an old journal; user cleaned/edited the state dir; partial restore from backup tools; mixed state directories from multiple Codex++ versions.","solutions":["Delete the stale `state_root/<key>` directory and rerun reconcile to rebuild journal+backups against the current runtime/contract.","Upgrade Codex++ so `RuntimeContract::pinned()`/ORIGINAL_SHA matches the journal's runtime version.","Restore the state directory contents (original.mjs, candidate file, journal) to a mutually consistent set from backup.","Do not edit journal fields (especially modified_nanos/schema) by hand."],"exampleFix":"// before (hand-editing to force recovery)\n# modified_nanos\": 1234567890 in journal.json\n// after\n# reset state and let Codex++ regenerate it\nrm -rf ~/.codex/plugins/state/<key> && codex reconcile --browser-enabled","handlingStrategy":"validation","validationCode":"// before enabling, confirm the journal set is self-consistent\nlet j: serde_json::Value = serde_json::from_slice(&std::fs::read(state_root.join(&key).join(\"journal.json\"))?)?;\nlet ok_schema = j[\"schema\"] == 1;\nlet ok_nanos = j[\"modified_nanos\"].as_u64().unwrap_or(u64::MAX) < 1_000_000_000;\nlet orig_sha = sha256(&std::fs::read(state_root.join(&key).join(\"original.mjs\"))?);\nif !(ok_schema && ok_nanos && orig_sha == j[\"original_sha\"].as_str().unwrap_or(\"\")) {\n    // inconsistent journal set: reset state dir\n}","typeGuard":null,"tryCatchPattern":"match reconcile(&paths, true) {\n    Err(e) if e.to_string().contains(\"conflicts with verified content\") => {\n        // journal does not match backups or the current runtime/contract:\n        std::fs::remove_dir_all(state_root.join(&key))?;\n        reconcile(&paths, true)?; // rebuild from a fresh snapshot\n    }\n    other => other?,\n}","preventionTips":["Upgrade Codex++ together with codex so pinned shas match journal versions","Restore the whole state dir atomically, never file-by-file","Never edit journal fields (schema, shas, modified_nanos)","Reset state_root/<key> after any external tool touches the backups"],"tags":["journal","integrity-check","recovery"],"backgroundTag":"checksum-mismatch","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}