BigPizzaV3/CodexPlusPlus · error

External runtime change prevents recovery

Error message

External runtime change prevents recovery

What it means

`restore_all` restores every journaled runtime back to its original (unpatched) content, but only if the current runtime file equals either the recorded original or the recorded candidate — the only two states Codex++ is responsible for. If the file holds anything else, an external actor modified the patched runtime and Codex++ cannot safely restore (it would destroy unknown changes or restore onto a foreign base), so it aborts.

Solutions

  1. Reinstall/repair the codex plugin cache so the runtime returns to a known (original) state, then run reconcile(disabled) again.
  2. Delete the obsolete runtime cache directory (desktop owns cache deletion) so restore_all skips it via the `!target.exists()` path.
  3. Clear `state_root/<key>` journal state once the runtime is consistent, then retry.
  4. Upgrade Codex++ if the runtime version changed so the contract/journal matches the new original.

Example fix

// before
# service.mjs was hand-patched while enabled
reconcile(&paths, false)?;  // -> External runtime change prevents recovery
// after
# let codex reinstall the plugin cache first
codex repair-plugins
reconcile(&paths, false)?;
Defensive patterns

Strategy: try-catch

Validate before calling

// before disabling, confirm each journaled runtime is in a known state
let current = std::fs::read(runtime_root.join(&key).join("service.mjs"))?;
let original = std::fs::read(state_root.join(&key).join("original.mjs"))?;
if current != original && current != candidate_bytes {
    // external modification detected: repair/reinstall the plugin cache first
}

Try / catch

match reconcile(&paths, false) {
    Err(e) if e.to_string().contains("External runtime change prevents recovery") => {
        // repair the plugin cache (codex reinstall/repair) so the runtime
        // returns to a known state, then retry the disable
        codex_repair_plugins()?;
        reconcile(&paths, false)?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: `restore_all` (from `reconcile_locked`, e.g. disabling the feature) reads a target service file whose bytes match neither `original` nor `candidate` from the journal — the runtime was edited/replaced externally while patch state existed.

Common situations: Codex/plugin auto-update replaced the service file between enable and disable; user or another tool patched the .mjs directly; a partial external write left the file in a mixed state.

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

Appendix: source

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

    for entry in fs::read_dir(&paths.state_root)? {
        let entry = entry?;
        let key = entry.file_name().to_string_lossy().to_string();
        if !key_valid(&key) || keep == Some(key.as_str()) {
            continue;
        }
        let dir = entry.path();
        plain_path(&dir)?;
        if !dir.join("journal.json").exists() {
            continue;
        }
        let target = paths.runtime_root.join(&key).join(SERVICE);
        if !target.exists() {
            continue; // Desktop owns cache deletion; never resurrect an obsolete runtime.
        }
        let (journal, original, candidate) = recovery_material(paths, &key, contract)?;
        guards.extend(pin_parents(&target)?);
        let current = read_regular(&target, MAX_SERVICE)?;
        ensure!(
            current == original || current == candidate,
            "External runtime change prevents recovery"
        );
        if current == candidate {
            let modified = UNIX_EPOCH
                .checked_add(Duration::new(journal.modified_secs, journal.modified_nanos))
                .context("Invalid recovery timestamp")?;
            pending.push((target, modified, original, current));
        }
    }
    // Preflight every cache before restoring any, independent of directory enumeration order.
    for (target, modified, original, current) in pending {
        ensure!(
            read_regular(&target, MAX_SERVICE)? == current,
            "Concurrent recovery change"
        );
        atomic_write_with_modified(&target, &original, Some(modified))?;
        ensure!(

View on GitHub (pinned to b1ed92e5e4)