{"record":{"id":"e541c7873296c05d","repo":"BigPizzaV3/CodexPlusPlus","slug":"codex-global-state-json-changed-while-restoring-sidebar","errorCode":null,"errorMessage":".codex-global-state.json changed while restoring sidebar state","messagePattern":"\\.codex-global-state\\.json changed while restoring sidebar state","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-data/src/provider_sync.rs","lineNumber":2896,"sourceCode":"    }\n    if let Some(entries) = global.get(\"atom_entries\").and_then(Value::as_object) {\n        let atom = root\n            .entry(\"electron-persisted-atom-state\")\n            .or_insert_with(|| Value::Object(Map::new()));\n        if let Some(atom) = atom.as_object_mut() {\n            for (key, value) in entries {\n                if !atom.contains_key(key) {\n                    atom.insert(key.clone(), value.clone());\n                    restored += 1;\n                }\n            }\n        }\n    }\n    if restored == 0 {\n        return Ok(0);\n    }\n    if path.exists() && fs::read(&path)? != original_bytes {\n        anyhow::bail!(\".codex-global-state.json changed while restoring sidebar state\");\n    }\n    codex_plus_core::settings::atomic_write(&path, serde_json::to_string_pretty(&state)?.as_bytes())?;\n    Ok(restored)\n}\n\nfn restore_missing_map_entries(root: &mut Map<String, Value>, maps: &Map<String, Value>) -> usize {\n    let mut restored = 0usize;\n    for (key, entries) in maps {\n        let value = root\n            .entry(key.clone())\n            .or_insert_with(|| Value::Object(Map::new()));\n        if let (Some(target), Some(entries)) = (value.as_object_mut(), entries.as_object()) {\n            for (entry_key, entry_value) in entries {\n                if !target.contains_key(entry_key) {\n                    target.insert(entry_key.clone(), entry_value.clone());\n                    restored += 1;\n                }\n            }","sourceCodeStart":2878,"sourceCodeEnd":2914,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-data/src/provider_sync.rs#L2878-L2914","documentation":"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.","triggerScenarios":"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.","commonSituations":"Two app instances running simultaneously; Codex itself persisting state while restore is in progress; sync/backup tools touching the file during restore.","solutions":["Close other Codex/manager instances and rerun the restore so the file is stable during the operation","Retry the restore — the check is a lost-update guard, so a rerun on a quiet system will succeed","If it keeps failing, back up the current file, remove stale state, and let a fresh sync rebuild sidebar state"],"exampleFix":"// before\nrestore_sidebar_state(&path, ...)?; // may hit concurrent write\n// after\nloop {\n    match restore_sidebar_state(&path, ...) {\n        Ok(n) => { break; }\n        Err(e) if e.to_string().contains(\"changed while restoring\") => {\n            std::thread::sleep(Duration::from_millis(500)); // retry after quiescing writers\n        }\n        Err(e) => return Err(e),\n    }\n}","handlingStrategy":"retry","validationCode":"let before = std::fs::read(&path)?;\n// ... perform restore ...\nif std::fs::read(&path)? != before {\n    eprintln!(\"file changed concurrently; retry restore\");\n}","typeGuard":null,"tryCatchPattern":"match restore_sidebar_state(&path, snapshot) {\n    Err(e) if e.to_string().contains(\"changed while restoring\") => {\n        std::thread::sleep(Duration::from_millis(500));\n        restore_sidebar_state(&path, snapshot)?; // retry once file is stable\n    }\n    other => other?,\n}","preventionTips":["Ensure only one process writes .codex-global-state.json at a time (single-instance lock)","Quiesce Codex CLI/manager before running restore","Back up the state file before restore operations"],"tags":["concurrency","state","lost-update"],"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"}