{"record":{"id":"182be6e8d80a9e29","repo":"BigPizzaV3/CodexPlusPlus","slug":"runtime-changed-outside-codex-refusing-to-overwrite","errorCode":null,"errorMessage":"Runtime changed outside Codex++; refusing to overwrite","messagePattern":"Runtime changed outside Codex\\+\\+; refusing to overwrite","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/native_browser.rs","lineNumber":445,"sourceCode":"        }\n        let modified = fs::metadata(&target)?\n            .modified()?\n            .duration_since(UNIX_EPOCH)?;\n        let journal = Journal {\n            schema: 1,\n            original_sha: contract.service_sha.clone(),\n            candidate_sha: sha(&candidate),\n            modified_secs: modified.as_secs(),\n            modified_nanos: modified.subsec_nanos(),\n        };\n        // Durable original and journal precede any runtime write.\n        atomic_write(&journal_path, &serde_json::to_vec(&journal)?)?;\n    }\n    let (journal, original, candidate) = recovery_material(paths, key, contract)?;\n    if current == candidate {\n        return Ok(());\n    }\n    ensure!(\n        current == original && sha(&current) == journal.original_sha,\n        \"Runtime changed outside Codex++; refusing to overwrite\"\n    );\n    ensure!(\n        read_regular(&target, MAX_SERVICE)? == current,\n        \"Concurrent runtime change\"\n    );\n    atomic_write(&target, &candidate)?;\n    ensure!(\n        read_regular(&target, MAX_SERVICE)? == candidate,\n        \"Runtime write verification failed\"\n    );\n    Ok(())\n}\n\nfn recovery_material(\n    paths: &BrowserPaths,\n    key: &str,","sourceCodeStart":427,"sourceCodeEnd":463,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/native_browser.rs#L427-L463","documentation":"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.","triggerScenarios":"`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`.","commonSituations":"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.","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."],"exampleFix":"// before\nreconcile(&paths, true)  // contract pins old service_sha after plugin update\n// after\n# restore first, then reconcile against the new runtime\nreconcile(&paths, false)?;\n// update RuntimeContract::pinned() / upgrade codex, then:\nreconcile(&paths, true)?;","handlingStrategy":"try-catch","validationCode":"let journal: Journal = serde_json::from_slice(&std::fs::read(state_root.join(&key).join(\"journal.json\"))?)?;\nlet current = std::fs::read(runtime_root.join(&key).join(\"service.mjs\"))?;\nif sha256(&current) != journal.original_sha && sha256(&current) != contract.service_sha {\n    // runtime drifted from journal: restore/disable first before re-enabling\n}","typeGuard":null,"tryCatchPattern":"match reconcile(&paths, true) {\n    Err(e) if e.to_string().contains(\"Runtime changed outside Codex++\") => {\n        // recover to original, refresh contract/runtime, then re-enable\n        reconcile(&paths, false)?;\n        // update RuntimeContract::pinned() / upgrade codex here\n        reconcile(&paths, true)?;\n    }\n    other => other?,\n}","preventionTips":["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)"],"tags":["state-management","external-modification","native-browser"],"backgroundTag":"checksum-mismatch","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}