{"record":{"id":"ad29fa4460f622d6","repo":"BigPizzaV3/CodexPlusPlus","slug":"sidebar-snapshot-is-missing-thread-id","errorCode":null,"errorMessage":"sidebar snapshot is missing thread_id","messagePattern":"sidebar snapshot is missing thread_id","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-data/src/provider_sync.rs","lineNumber":2697,"sourceCode":"    validate_thread_sidebar_snapshot(codex_home, snapshot)?;\n    let thread_id = snapshot\n        .get(\"thread_id\")\n        .and_then(Value::as_str)\n        .unwrap_or_default();\n    let mut restored = restore_thread_to_global_state(codex_home, thread_id, snapshot)?;\n    restored += restore_thread_to_catalog_dbs(codex_home, thread_id, snapshot)?;\n    Ok(restored)\n}\n\npub fn validate_thread_sidebar_snapshot(\n    codex_home: &Path,\n    snapshot: &Value,\n) -> anyhow::Result<()> {\n    let thread_id = snapshot\n        .get(\"thread_id\")\n        .and_then(Value::as_str)\n        .filter(|id| !id.trim().is_empty())\n        .ok_or_else(|| anyhow::anyhow!(\"sidebar snapshot is missing thread_id\"))?;\n    if let Some(catalog) = snapshot.get(\"catalog\") {\n        let entries = catalog\n            .as_array()\n            .ok_or_else(|| anyhow::anyhow!(\"sidebar snapshot catalog must be an array\"))?;\n        let allowed_paths = sidebar_catalog_db_paths(codex_home)?;\n        for entry in entries {\n            let table = entry\n                .get(\"table\")\n                .and_then(Value::as_str)\n                .ok_or_else(|| anyhow::anyhow!(\"sidebar catalog entry is missing table\"))?;\n            if !SIDEBAR_CATALOG_TABLES.contains(&table) {\n                anyhow::bail!(\"unsupported sidebar catalog table: {table}\");\n            }\n            let path = entry\n                .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\"))?;","sourceCodeStart":2679,"sourceCodeEnd":2715,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-data/src/provider_sync.rs#L2679-L2715","documentation":"The sidebar snapshot restore path validates that a snapshot JSON contains a non-empty string thread_id before touching any catalog databases. If the field is absent, not a string, or blank, the restore is rejected with this error. It is a guard so row filtering in the catalog is always anchored to a valid thread identity.","triggerScenarios":"Calling the sidebar snapshot restore function with a snapshot Value that lacks snapshot[\"thread_id\"], has it as a non-string (e.g. number), or as an empty/whitespace-only string.","commonSituations":"Hand-edited or truncated snapshot JSON; a snapshot exported by an older/newer version with a renamed field; snapshot produced by a failed export that omitted thread_id; copying a partial snapshot file.","solutions":["Add a valid non-empty \"thread_id\" string field to the snapshot JSON at the top level","Re-export the sidebar snapshot from the manager so the field is populated","Validate the snapshot JSON schema before attempting restore (jq '.thread_id' snapshot.json)","Check for version skew: confirm the snapshot was produced by the same CodexPlusPlus version"],"exampleFix":"// before\n{ \"catalog\": [ ... ] }\n// after\n{ \"thread_id\": \"3f9c...\", \"catalog\": [ ... ] }","handlingStrategy":"validation","validationCode":"function hasThreadId(snapshot) {\n  return typeof snapshot?.thread_id === 'string' && snapshot.thread_id.trim() !== '';\n}\nif (!hasThreadId(snapshot)) throw new Error('snapshot has no usable thread_id');","typeGuard":"function isValidSnapshot(v) {\n  return typeof v === 'object' && v !== null &&\n    typeof v.thread_id === 'string' && v.thread_id.trim() !== '';\n}","tryCatchPattern":"try {\n  restore_sidebar_snapshot(&snapshot, codex_home)?;\n} catch (e) {\n  if (String(e).includes('missing thread_id')) {\n    // reject/re-export the snapshot\n    reportInvalidSnapshot(snapshot);\n  }\n}","preventionTips":["Validate snapshot JSON schema right after export","Never hand-edit snapshots; always re-export","Include thread_id in any custom export tooling","Version-stamp snapshots and check compatibility before restore"],"tags":["json","validation","sidebar","snapshot"],"backgroundTag":"missing-required-argument","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"}