{"record":{"id":"b5cf7397ed61dc39","repo":"BigPizzaV3/CodexPlusPlus","slug":"runtime-changed-outside-codex","errorCode":null,"errorMessage":"Runtime changed outside Codex++","messagePattern":"Runtime changed outside Codex\\+\\+","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/native_browser.rs","lineNumber":404,"sourceCode":"    if journal_path.exists() {\n        let (journal, original, recorded_candidate) = recovery_material(paths, key, contract)?;\n        let candidate = transform(&original, &control, contract)?;\n        if current == recorded_candidate {\n            if candidate == recorded_candidate {\n                return Ok(());\n            }\n            // Restore before upgrading the journal, so either journal can recover a crash.\n            ensure!(\n                read_regular(&target, MAX_SERVICE)? == current,\n                \"Concurrent adapter upgrade\"\n            );\n            let modified = UNIX_EPOCH\n                .checked_add(Duration::new(journal.modified_secs, journal.modified_nanos))\n                .context(\"Invalid recovery timestamp\")?;\n            atomic_write_with_modified(&target, &original, Some(modified))?;\n            current = original;\n        }\n        ensure!(\n            sha(&current) == contract.service_sha,\n            \"Runtime changed outside Codex++\"\n        );\n    }\n    {\n        let candidate = transform(&current, &control, contract)?;\n        if backup.exists() {\n            ensure!(\n                read_regular(&backup, MAX_SERVICE)? == current,\n                \"Unjournaled backup conflict\"\n            );\n        } else {\n            write_new(&backup, &current)?;\n        }\n        let candidate_path = backup_dir.join(format!(\"candidate-{}.mjs\", sha(&candidate)));\n        if candidate_path.exists() {\n            ensure!(\n                read_regular(&candidate_path, MAX_SERVICE)? == candidate,","sourceCodeStart":386,"sourceCodeEnd":422,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/native_browser.rs#L386-L422","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let current = read_regular(&target, MAX_SERVICE)?;\nif sha(&current) != contract.service_sha {\n    eprintln!(\"service file does not match contract (sha {:?}); reinstall runtime before patching\", sha(&current));\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = prepare(&paths, &key, &contract) {\n    if e.to_string().contains(\"Runtime changed outside Codex++\") {\n        restore_pristine_service_from_plugin_package(&runtime)?; // or reinstall plugin\n        prepare(&paths, &key, &contract)?;\n    } else { return Err(e); }\n}","preventionTips":["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"],"tags":["integrity","checksum","native-browser","external-modification"],"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"}