BigPizzaV3/CodexPlusPlus · error
sidebar catalog entry rows must be an array
Error message
sidebar catalog entry rows must be an array
What it means
Each sidebar catalog entry must supply its payload rows as a JSON array under "rows". If the field is missing or not an array, the restore cannot iterate rows and aborts before writing anything to the database.
Solutions
- Make "rows" a JSON array of row objects in the entry
- Use an empty array "rows": [] if there is nothing to restore for this entry
- Re-export the snapshot from the app to get the correct rows shape
- Validate with jq: jq '.catalog[0].rows | type' should print "array"
Example fix
// before
"rows": { "abc": { "thread_id": "..." } }
// after
"rows": [ { "thread_id": "..." } ] Defensive patterns
Strategy: validation
Validate before calling
for (const e of snapshot.catalog ?? []) {
if (!Array.isArray(e?.rows)) throw new Error('entry rows must be an array');
} Type guard
function entryHasRows(e) {
return typeof e === 'object' && e !== null && Array.isArray(e.rows);
} Try / catch
try {
restore_sidebar_snapshot(&snapshot, codex_home)?;
} catch (e) {
if (String(e).includes('rows must be an array')) {
normalizeRowsToArray(snapshot); // convert or default to [], retry
}
} Prevention
- Always serialize rows as arrays in exporters
- Default missing rows to [] rather than omitting the key
- Validate snapshot shape before restore
- Keep row schema tests alongside export code
When it happens
Trigger: Catalog entry where snapshot entry["rows"] is absent, or is not an array (object, string, number, null), passed to the restore function.
Common situations: Hand-crafted snapshot with rows encoded as an object keyed by id; snapshot truncated mid-write; exporter version emitting a different rows container; rows stripped when editing the file.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- sidebar catalog entry is missing table
- sidebar catalog entry is missing db_path
- sidebar catalog row is missing thread_id
- sidebar snapshot catalog must be an array
- catalog JSON 解析失败:
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/ce5018f0064967f1.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-data/src/provider_sync.rs:2723
.get("table")
.and_then(Value::as_str)
.ok_or_else(|| anyhow::anyhow!("sidebar catalog entry is missing table"))?;
if !SIDEBAR_CATALOG_TABLES.contains(&table) {
anyhow::bail!("unsupported sidebar catalog table: {table}");
}
let path = entry
.get("db_path")
.and_then(Value::as_str)
.map(PathBuf::from)
.ok_or_else(|| anyhow::anyhow!("sidebar catalog entry is missing db_path"))?;
let canonical = fs::canonicalize(&path)?;
if !allowed_paths.contains(&canonical) {
anyhow::bail!("sidebar catalog database is not an allowed Codex database");
}
let rows = entry
.get("rows")
.and_then(Value::as_array)
.ok_or_else(|| anyhow::anyhow!("sidebar catalog entry rows must be an array"))?;
for row in rows {
let row_id = row
.get("thread_id")
.and_then(Value::as_str)
.ok_or_else(|| anyhow::anyhow!("sidebar catalog row is missing thread_id"))?;
if row_id != thread_id {
anyhow::bail!("sidebar catalog row thread_id does not match snapshot");
}
}
}
}
Ok(())
}
const SIDEBAR_CATALOG_TABLES: [&str; 3] = [
"local_thread_catalog",
"thread_timeline_ledger",
"local_thread_catalog_scan_entries",View on GitHub (pinned to b1ed92e5e4)