BigPizzaV3/CodexPlusPlus · error

Candidate backup conflict

Error message

Candidate backup conflict

What it means

In `prepare`, the transformed candidate is stored as `state_root/<key>/candidate-<sha>.mjs`. If that file already exists, its content must equal the freshly computed candidate; a hash-named file with mismatching content means the candidate cache is corrupt or the hash naming invariant was violated (content no longer matches its own sha name). Codex++ aborts instead of silently reusing or overwriting a bad candidate.

Solutions

  1. Delete the corrupt `candidate-<sha>.mjs` file and rerun reconcile so it is rewritten from the transform output.
  2. Reset the entire `state_root/<key>` directory and rerun reconcile.
  3. Exclude the Codex++ state directory from file-sync tools that can rewrite files in place.
  4. Verify the file's sha256 actually differs from its filename; if it matches, the transform is non-deterministic — check `control.json`.

Example fix

// before (shell)
# editing the candidate file by hand
vim ~/.codex/plugins/state/<key>/candidate-<sha>.mjs
// after
# never edit; delete and let reconcile regenerate
rm ~/.codex/plugins/state/<key>/candidate-<sha>.mjs
Defensive patterns

Strategy: validation

Validate before calling

let candidate_path = state_root.join(key).join(format!("candidate-{}.mjs", expected_sha));
if candidate_path.exists() {
    let on_disk = std::fs::read(&candidate_path)?;
    if sha256(&on_disk) != expected_sha { /* corrupt: delete the candidate file before reconcile */ }
}

Try / catch

match reconcile(&paths, true) {
    Err(e) if e.to_string().contains("Candidate backup conflict") => {
        for entry in std::fs::read_dir(state_root.join(&key))?
            .filter_map(Result::ok)
            .filter(|e| e.file_name().to_string_lossy().starts_with("candidate-"))
        { let _ = std::fs::remove_file(entry.path()); }
        reconcile(&paths, true)?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: `reconcile(paths, true)` path where `candidate-<sha(&candidate)>.mjs` already exists in the backup dir and `read_regular(candidate_path) != candidate`. Requires the file content to diverge from the sha in its filename — e.g. partial write, manual edit, or a tool that rewrote the file in place.

Common situations: Manual editing or 'cleaning' of the state directory; a crashed/partial `write_new` followed by a disk tool recovering the wrong bytes; a shared/synced state dir (Dropbox etc.) merging conflicting versions.

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

Appendix: source

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

        }
        ensure!(
            sha(&current) == contract.service_sha,
            "Runtime changed outside Codex++"
        );
    }
    {
        let candidate = transform(&current, &control, contract)?;
        if backup.exists() {
            ensure!(
                read_regular(&backup, MAX_SERVICE)? == current,
                "Unjournaled backup conflict"
            );
        } else {
            write_new(&backup, &current)?;
        }
        let candidate_path = backup_dir.join(format!("candidate-{}.mjs", sha(&candidate)));
        if candidate_path.exists() {
            ensure!(
                read_regular(&candidate_path, MAX_SERVICE)? == candidate,
                "Candidate backup conflict"
            );
        } else {
            write_new(&candidate_path, &candidate)?;
        }
        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)?)?;

View on GitHub (pinned to b1ed92e5e4)