BigPizzaV3/CodexPlusPlus · error

Runtime changed outside Codex++; refusing to overwrite

Error message

Runtime changed outside Codex++; refusing to overwrite

What it means

After journaling, `prepare` re-reads the current runtime file and requires it to equal both the saved `original.mjs` and the journal's `original_sha` before overwriting it with the candidate. If the runtime file now differs from the pristine original recorded in the journal, something outside Codex++ modified the runtime between journaling and patching, and overwriting would destroy unknown changes and break crash-recovery guarantees, so it refuses.

Solutions

  1. Update/reinstall the codex plugin so the runtime matches the contract, or upgrade Codex++ so `RuntimeContract` pins the new runtime shas.
  2. Run `reconcile(paths, false)` to disable and restore the original, then re-enable after the runtime is consistent.
  3. Delete the stale `state_root/<key>` journal state and rerun reconcile once the runtime matches the current contract.
  4. Do not hand-edit files under the plugin cache.

Example fix

// before
reconcile(&paths, true)  // contract pins old service_sha after plugin update
// after
# restore first, then reconcile against the new runtime
reconcile(&paths, false)?;
// update RuntimeContract::pinned() / upgrade codex, then:
reconcile(&paths, true)?;
Defensive patterns

Strategy: try-catch

Validate before calling

let journal: Journal = serde_json::from_slice(&std::fs::read(state_root.join(&key).join("journal.json"))?)?;
let current = std::fs::read(runtime_root.join(&key).join("service.mjs"))?;
if sha256(&current) != journal.original_sha && sha256(&current) != contract.service_sha {
    // runtime drifted from journal: restore/disable first before re-enabling
}

Try / catch

match reconcile(&paths, true) {
    Err(e) if e.to_string().contains("Runtime changed outside Codex++") => {
        // recover to original, refresh contract/runtime, then re-enable
        reconcile(&paths, false)?;
        // update RuntimeContract::pinned() / upgrade codex here
        reconcile(&paths, true)?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: `prepare` (via `reconcile(paths, true)`) reaches the final write phase while `current != original || sha(&current) != journal.original_sha` — e.g. codex/the plugin updater rewrote the service file after the state snapshot was taken, or the journal references a runtime version that no longer matches `contract.service_sha`.

Common situations: Codex desktop auto-updated the unified-computer-use plugin between two reconcile runs while browser patching was enabled; the user reinstalled or repaired the plugin cache; contract pinned sha is stale after a runtime upgrade.

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/182be6e8d80a9e29. Report an issue: GitHub.

Appendix: source

Thrown at crates/codex-plus-core/src/native_browser.rs:445

        }
        let modified = fs::metadata(&target)?
            .modified()?
            .duration_since(UNIX_EPOCH)?;
        let journal = Journal {
            schema: 1,
            original_sha: contract.service_sha.clone(),
            candidate_sha: sha(&candidate),
            modified_secs: modified.as_secs(),
            modified_nanos: modified.subsec_nanos(),
        };
        // Durable original and journal precede any runtime write.
        atomic_write(&journal_path, &serde_json::to_vec(&journal)?)?;
    }
    let (journal, original, candidate) = recovery_material(paths, key, contract)?;
    if current == candidate {
        return Ok(());
    }
    ensure!(
        current == original && sha(&current) == journal.original_sha,
        "Runtime changed outside Codex++; refusing to overwrite"
    );
    ensure!(
        read_regular(&target, MAX_SERVICE)? == current,
        "Concurrent runtime change"
    );
    atomic_write(&target, &candidate)?;
    ensure!(
        read_regular(&target, MAX_SERVICE)? == candidate,
        "Runtime write verification failed"
    );
    Ok(())
}

fn recovery_material(
    paths: &BrowserPaths,
    key: &str,

View on GitHub (pinned to b1ed92e5e4)