BigPizzaV3/CodexPlusPlus · error
Runtime changed outside Codex++
Error message
Runtime changed outside Codex++
What it means
After restoring the journaled original, prepare() asserts that the service file's SHA matches the contract's expected service_sha. If it doesn't, the runtime service file was changed by something other than Codex++'s patch/restore cycle (external modification or version drift), and Codex++ refuses to patch on top of an unknown base to keep its backup/restore guarantees intact.
Solutions
- Refresh the runtime: reinstall/update the plugin so the on-disk service matches the current contract's service_sha, then re-run reconcile
- If Codex++ is older than the runtime, upgrade Codex++ so its contract ships the new service_sha
- Restore the pristine service file from the plugin package (never hand-edit) and retry
Defensive patterns
Strategy: validation
Validate before calling
let current = read_regular(&target, MAX_SERVICE)?;
if sha(¤t) != contract.service_sha {
eprintln!("service file does not match contract (sha {:?}); reinstall runtime before patching", sha(¤t));
} Try / catch
if let Err(e) = prepare(&paths, &key, &contract) {
if e.to_string().contains("Runtime changed outside Codex++") {
restore_pristine_service_from_plugin_package(&runtime)?; // or reinstall plugin
prepare(&paths, &key, &contract)?;
} else { return Err(e); }
} Prevention
- Never hand-edit files under the runtime cache's node_modules
- After a plugin/browser update, re-run reconcile so a fresh contract is applied before patching
- Pin plugin versions when Codex++ contracts are known to match them
When it happens
Trigger: prepare() with a journal: current (after possible restore) hashes to something != contract.service_sha — e.g. the plugin updated browser-desktop between Codex++ runs so the original file no longer matches the contract, or a user/tool edited the service bundle.
Common situations: Browser/plugin auto-update changing the bundled service between Codex++ sessions; manual hot-fixes inside node_modules; partial upgrade leaving a mixed-version service file.
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
- Unsupported native runtime component
- SHA-256 校验失败
- Runtime changed outside Codex++; refusing to overwrite
- Ambiguous runtime selection; no cache was modified
- Concurrent adapter upgrade
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/b5cf7397ed61dc39.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/native_browser.rs:404
if journal_path.exists() {
let (journal, original, recorded_candidate) = recovery_material(paths, key, contract)?;
let candidate = transform(&original, &control, contract)?;
if current == recorded_candidate {
if candidate == recorded_candidate {
return Ok(());
}
// Restore before upgrading the journal, so either journal can recover a crash.
ensure!(
read_regular(&target, MAX_SERVICE)? == current,
"Concurrent adapter upgrade"
);
let modified = UNIX_EPOCH
.checked_add(Duration::new(journal.modified_secs, journal.modified_nanos))
.context("Invalid recovery timestamp")?;
atomic_write_with_modified(&target, &original, Some(modified))?;
current = original;
}
ensure!(
sha(¤t) == contract.service_sha,
"Runtime changed outside Codex++"
);
}
{
let candidate = transform(¤t, &control, contract)?;
if backup.exists() {
ensure!(
read_regular(&backup, MAX_SERVICE)? == current,
"Unjournaled backup conflict"
);
} else {
write_new(&backup, ¤t)?;
}
let candidate_path = backup_dir.join(format!("candidate-{}.mjs", sha(&candidate)));
if candidate_path.exists() {
ensure!(
read_regular(&candidate_path, MAX_SERVICE)? == candidate,View on GitHub (pinned to b1ed92e5e4)