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

  1. Delete the stale `state_root/<key>` directory and rerun reconcile to rebuild journal+backups against the current runtime/contract.
  2. Upgrade Codex++ so `RuntimeContract::pinned()`/ORIGINAL_SHA matches the journal's runtime version.
  3. Restore the state directory contents (original.mjs, candidate file, journal) to a mutually consistent set from backup.
  4. 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

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


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)