{"record":{"id":"27ff68504e63edd4","repo":"BigPizzaV3/CodexPlusPlus","slug":"sidebar-catalog-row-thread-id-does-not-match-snapshot","errorCode":null,"errorMessage":"sidebar catalog row thread_id does not match snapshot","messagePattern":"sidebar catalog row thread_id does not match snapshot","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-data/src/provider_sync.rs","lineNumber":2730,"sourceCode":"                .get(\"db_path\")\n                .and_then(Value::as_str)\n                .map(PathBuf::from)\n                .ok_or_else(|| anyhow::anyhow!(\"sidebar catalog entry is missing db_path\"))?;\n            let canonical = fs::canonicalize(&path)?;\n            if !allowed_paths.contains(&canonical) {\n                anyhow::bail!(\"sidebar catalog database is not an allowed Codex database\");\n            }\n            let rows = entry\n                .get(\"rows\")\n                .and_then(Value::as_array)\n                .ok_or_else(|| anyhow::anyhow!(\"sidebar catalog entry rows must be an array\"))?;\n            for row in rows {\n                let row_id = row\n                    .get(\"thread_id\")\n                    .and_then(Value::as_str)\n                    .ok_or_else(|| anyhow::anyhow!(\"sidebar catalog row is missing thread_id\"))?;\n                if row_id != thread_id {\n                    anyhow::bail!(\"sidebar catalog row thread_id does not match snapshot\");\n                }\n            }\n        }\n    }\n    Ok(())\n}\n\nconst SIDEBAR_CATALOG_TABLES: [&str; 3] = [\n    \"local_thread_catalog\",\n    \"thread_timeline_ledger\",\n    \"local_thread_catalog_scan_entries\",\n];\n\nfn snapshot_thread_from_global_state(\n    codex_home: &Path,\n    thread_id: &str,\n) -> anyhow::Result<Value> {\n    let path = codex_home.join(\".codex-global-state.json\");","sourceCodeStart":2712,"sourceCodeEnd":2748,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-data/src/provider_sync.rs#L2712-L2748","documentation":"Every catalog row must carry a thread_id identical to the snapshot's thread_id being restored. This error is thrown when a row's thread_id differs, i.e. the catalog rows do not correspond to the snapshot, so restoring would attach rows to the wrong thread.","triggerScenarios":"Restoring sidebar state where any row in an entry has a 'thread_id' string that does not equal the snapshot thread id — mixed state from concurrent edits, stale rows after thread deletion/recreation, or manual JSON editing.","commonSituations":"Sidebar data changed between snapshot and restore; rows copied from another snapshot; UUIDs regenerated after re-login or session reset; partially written state from a crash.","solutions":["Remove or fix rows whose thread_id disagrees with the snapshot, then retry the restore","Rebuild the sidebar snapshot from the current database and restore from that fresh snapshot instead of the stale one","Check for concurrent Codex instances writing .codex-global-state.json during restore; close other instances and retry"],"exampleFix":"// before\n{\"thread_id\": \"aaa-old\", \"rows\": [{\"thread_id\": \"bbb-new\"}]}\n// after\n{\"thread_id\": \"bbb-new\", \"rows\": [{\"thread_id\": \"bbb-new\"}]}","handlingStrategy":"validation","validationCode":"fn rows_match(entry: &Value, thread_id: &str) -> bool {\n    entry.get(\"rows\").and_then(Value::as_array).map(|rows| {\n        rows.iter().all(|r| r.get(\"thread_id\").and_then(Value::as_str) == Some(thread_id))\n    }).unwrap_or(false)\n}","typeGuard":"fn row_thread<'a>(row: &'a Value, expected: &str) -> Option<&'a str> {\n    row.get(\"thread_id\").and_then(Value::as_str).filter(|t| *t == expected)\n}","tryCatchPattern":"if let Err(e) = restore(&state) {\n    if e.to_string().contains(\"does not match snapshot\") {\n        // discard stale snapshot and rebuild from the live database\n    }\n}","preventionTips":["Snapshot and restore without letting other Codex instances write in between","Filter rows by thread_id before persisting catalog entries","Regenerate snapshots after thread deletion/recreation"],"tags":["consistency","validation","state"],"backgroundTag":"schema-validation-failed","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"}