BigPizzaV3/CodexPlusPlus · error
Recovery journal conflicts with verified content
Error message
Recovery journal conflicts with verified content
What it means
`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.
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.
Example fix
// before (hand-editing to force recovery) # modified_nanos": 1234567890 in journal.json // after # reset state and let Codex++ regenerate it rm -rf ~/.codex/plugins/state/<key> && codex reconcile --browser-enabled
Defensive patterns
Strategy: validation
Validate before calling
// before enabling, confirm the journal set is self-consistent
let j: serde_json::Value = serde_json::from_slice(&std::fs::read(state_root.join(&key).join("journal.json"))?)?;
let ok_schema = j["schema"] == 1;
let ok_nanos = j["modified_nanos"].as_u64().unwrap_or(u64::MAX) < 1_000_000_000;
let orig_sha = sha256(&std::fs::read(state_root.join(&key).join("original.mjs"))?);
if !(ok_schema && ok_nanos && orig_sha == j["original_sha"].as_str().unwrap_or("")) {
// inconsistent journal set: reset state dir
} Try / catch
match reconcile(&paths, true) {
Err(e) if e.to_string().contains("conflicts with verified content") => {
// journal does not match backups or the current runtime/contract:
std::fs::remove_dir_all(state_root.join(&key))?;
reconcile(&paths, true)?; // rebuild from a fresh snapshot
}
other => other?,
} Prevention
- 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
When it happens
Trigger: 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`.
Common situations: 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.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- Invalid candidate hash
- Concurrent recovery change
- DreamSkin 主题版本与请求不一致
- DreamSkin 主题包实际大小与审核元数据不一致
- DreamSkin 主题包 SHA-256 与审核元数据不一致
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/c5e8eb50eacaa456.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/native_browser.rs:479
fn recovery_material(
paths: &BrowserPaths,
key: &str,
contract: &RuntimeContract,
) -> Result<(Journal, Vec<u8>, Vec<u8>)> {
ensure!(key_valid(key), "Invalid recovery key");
let dir = paths.state_root.join(key);
let journal: Journal = serde_json::from_slice(&read_regular(&dir.join("journal.json"), 4096)?)?;
let original = read_regular(&dir.join("original.mjs"), MAX_SERVICE)?;
ensure!(
journal.candidate_sha.len() == 64
&& journal.candidate_sha.bytes().all(|b| b.is_ascii_hexdigit()),
"Invalid candidate hash"
);
let candidate = read_regular(
&dir.join(format!("candidate-{}.mjs", journal.candidate_sha)),
MAX_SERVICE,
)?;
ensure!(
journal.schema == 1
&& (journal.original_sha == contract.service_sha
|| journal.original_sha == ORIGINAL_SHA)
&& sha(&original) == journal.original_sha
&& journal.candidate_sha == sha(&candidate)
&& journal.modified_nanos < 1_000_000_000,
"Recovery journal conflicts with verified content"
);
Ok((journal, original, candidate))
}
fn restore_all(paths: &BrowserPaths, keep: Option<&str>, contract: &RuntimeContract) -> Result<()> {
let mut pending = Vec::new();
let mut guards = Vec::new();
for entry in fs::read_dir(&paths.state_root)? {
let entry = entry?;
let key = entry.file_name().to_string_lossy().to_string();
if !key_valid(&key) || keep == Some(key.as_str()) {View on GitHub (pinned to b1ed92e5e4)