BigPizzaV3/CodexPlusPlus · error

.codex-global-state.json changed while restoring sidebar…

Error message

.codex-global-state.json changed while restoring sidebar state

What it means

After restoring missing sidebar entries into the in-memory state, the code re-reads .codex-global-state.json and aborts if the file's bytes changed since it was originally read, to avoid a lost-update: writing back would clobber concurrent modifications. The error signals such a concurrent change was detected.

Solutions

  1. Close other Codex/manager instances and rerun the restore so the file is stable during the operation
  2. Retry the restore — the check is a lost-update guard, so a rerun on a quiet system will succeed
  3. If it keeps failing, back up the current file, remove stale state, and let a fresh sync rebuild sidebar state

Example fix

// before
restore_sidebar_state(&path, ...)?; // may hit concurrent write
// after
loop {
    match restore_sidebar_state(&path, ...) {
        Ok(n) => { break; }
        Err(e) if e.to_string().contains("changed while restoring") => {
            std::thread::sleep(Duration::from_millis(500)); // retry after quiescing writers
        }
        Err(e) => return Err(e),
    }
}
Defensive patterns

Strategy: retry

Validate before calling

let before = std::fs::read(&path)?;
// ... perform restore ...
if std::fs::read(&path)? != before {
    eprintln!("file changed concurrently; retry restore");
}

Try / catch

match restore_sidebar_state(&path, snapshot) {
    Err(e) if e.to_string().contains("changed while restoring") => {
        std::thread::sleep(Duration::from_millis(500));
        restore_sidebar_state(&path, snapshot)?; // retry once file is stable
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling the sidebar restore function while another process (Codex CLI, manager UI) rewrote .codex-global-state.json between the initial read and the final atomic_write.

Common situations: Two app instances running simultaneously; Codex itself persisting state while restore is in progress; sync/backup tools touching the file during restore.

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

Appendix: source

Thrown at crates/codex-plus-data/src/provider_sync.rs:2896

    }
    if let Some(entries) = global.get("atom_entries").and_then(Value::as_object) {
        let atom = root
            .entry("electron-persisted-atom-state")
            .or_insert_with(|| Value::Object(Map::new()));
        if let Some(atom) = atom.as_object_mut() {
            for (key, value) in entries {
                if !atom.contains_key(key) {
                    atom.insert(key.clone(), value.clone());
                    restored += 1;
                }
            }
        }
    }
    if restored == 0 {
        return Ok(0);
    }
    if path.exists() && fs::read(&path)? != original_bytes {
        anyhow::bail!(".codex-global-state.json changed while restoring sidebar state");
    }
    codex_plus_core::settings::atomic_write(&path, serde_json::to_string_pretty(&state)?.as_bytes())?;
    Ok(restored)
}

fn restore_missing_map_entries(root: &mut Map<String, Value>, maps: &Map<String, Value>) -> usize {
    let mut restored = 0usize;
    for (key, entries) in maps {
        let value = root
            .entry(key.clone())
            .or_insert_with(|| Value::Object(Map::new()));
        if let (Some(target), Some(entries)) = (value.as_object_mut(), entries.as_object()) {
            for (entry_key, entry_value) in entries {
                if !target.contains_key(entry_key) {
                    target.insert(entry_key.clone(), entry_value.clone());
                    restored += 1;
                }
            }

View on GitHub (pinned to b1ed92e5e4)