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
- Update/reinstall the codex plugin so the runtime matches the contract, or upgrade Codex++ so `RuntimeContract` pins the new runtime shas.
- Run `reconcile(paths, false)` to disable and restore the original, then re-enable after the runtime is consistent.
- Delete the stale `state_root/<key>` journal state and rerun reconcile once the runtime matches the current contract.
- 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(¤t) != journal.original_sha && sha256(¤t) != 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
- Keep RuntimeContract pins in sync with the installed codex/plugin version
- Disable the feature before upgrading codex or the plugin cache
- Never hand-edit plugin cache files
- After a codex upgrade, run reconcile(false) then reconcile(true)
When it happens
Trigger: `prepare` (via `reconcile(paths, true)`) reaches the final write phase while `current != original || sha(¤t) != 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
- External runtime change prevents recovery
- Runtime changed outside Codex++
- Unjournaled backup conflict
- Ambiguous runtime selection; no cache was modified
- Candidate backup conflict
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(¤t) == 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)