{"record":{"id":"188725761573e6da","repo":"BigPizzaV3/CodexPlusPlus","slug":"sidebar-catalog-entry-is-missing-table","errorCode":null,"errorMessage":"sidebar catalog entry is missing table","messagePattern":"sidebar catalog entry is missing table","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-data/src/provider_sync.rs","lineNumber":2707,"sourceCode":"pub 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\"))?;\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","sourceCodeStart":2689,"sourceCodeEnd":2725,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-data/src/provider_sync.rs#L2689-L2725","documentation":"Each entry inside a sidebar snapshot's catalog array must identify which SQLite table it applies to via a string \"table\" field. If an entry lacks the field or it is not a string, restore aborts with this error. The table name is then checked against the allow-list SIDEBAR_CATALOG_TABLES.","triggerScenarios":"A catalog array element without a \"table\" key (or with a non-string value) passed to the sidebar snapshot restore function.","commonSituations":"Hand-assembled snapshot missing per-entry metadata; schema drift between exporter versions (field renamed or moved); entries constructed programmatically with wrong keys (e.g. \"tableName\").","solutions":["Add \"table\": \"<allowed-table-name>\" to each catalog entry in the snapshot","Use a table name present in SIDEBAR_CATALOG_TABLES (e.g. the threads catalog table)","Re-export the snapshot from the app to get correctly shaped entries","Rename wrong keys (e.g. \"tableName\" -> \"table\") in the snapshot JSON"],"exampleFix":"// before\n{ \"db_path\": \"...\", \"rows\": [ ... ] }\n// after\n{ \"table\": \"thread_catalog\", \"db_path\": \"...\", \"rows\": [ ... ] }","handlingStrategy":"validation","validationCode":"const ALLOWED_TABLES = SIDEBAR_CATALOG_TABLES; // keep in sync with Rust allow-list\nfor (const entry of snapshot.catalog ?? []) {\n  if (typeof entry?.table !== 'string' || !ALLOWED_TABLES.includes(entry.table)) {\n    throw new Error(`bad catalog table: ${entry?.table}`);\n  }\n}","typeGuard":"function entryHasTable(e) {\n  return typeof e === 'object' && e !== null && typeof e.table === 'string';\n}","tryCatchPattern":"try {\n  restore_sidebar_snapshot(&snapshot, codex_home)?;\n} catch (e) {\n  if (String(e).includes('missing table')) {\n    reportEntryMissingField(snapshot, 'table');\n  }\n}","preventionTips":["Always set \"table\" when constructing catalog entries","Share a single constant/table-name enum between exporter and restorer","Validate entries before writing snapshot files","Avoid renaming the field across versions without a migration"],"tags":["json","schema","sidebar","catalog"],"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"}