BigPizzaV3/CodexPlusPlus · error
sidebar snapshot catalog must be an array
Error message
sidebar snapshot catalog must be an array
What it means
When a sidebar snapshot contains a "catalog" key, its value must be a JSON array of catalog entries. If the key exists but is not an array (object, string, null), the restore fails fast with this error before any database writes. The catalog is optional — absent means nothing to restore — but present-and-malformed is rejected.
Solutions
- Change "catalog" in the snapshot JSON to an array of entry objects
- Remove the "catalog" key entirely if there is nothing to restore (it is optional)
- Re-export the snapshot from CodexPlusPlus instead of hand-crafting it
- Validate with jq: jq '.catalog | type' should print "array"
Example fix
// before
"catalog": { "threads": [ ... ] }
// after
"catalog": [ { "table": "threads", "db_path": "...", "rows": [ ... ] } ] Defensive patterns
Strategy: validation
Validate before calling
if (snapshot.catalog !== undefined && !Array.isArray(snapshot.catalog)) {
throw new Error('snapshot.catalog must be an array');
} Type guard
function hasValidCatalog(v) {
return v.catalog === undefined || Array.isArray(v.catalog);
} Try / catch
try {
restore_sidebar_snapshot(&snapshot, codex_home)?;
} catch (e) {
if (String(e).includes('catalog must be an array')) {
fixOrDropCatalogField(snapshot); // drop key or convert to array, then retry
}
} Prevention
- Treat absent catalog as valid; never write catalog: null
- Emit catalog as an array in export code
- Run a JSON schema check on snapshots before restoring
- Keep export/restore versions in sync
When it happens
Trigger: Calling the restore function with snapshot["catalog"] set to a non-array JSON value, e.g. an object mapping table names to rows, a string, or null.
Common situations: Snapshot written by a different tool/version with a different catalog layout; manual JSON editing converted the array into an object; copy-paste error when assembling a snapshot by hand.
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 rows must be an array
- sidebar snapshot is missing thread_id
- 必须是 JSON 对象
- model_metadata 必须是 JSON 对象
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/17240a8ed60f0259.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-data/src/provider_sync.rs:2701
.unwrap_or_default();
let mut restored = restore_thread_to_global_state(codex_home, thread_id, snapshot)?;
restored += restore_thread_to_catalog_dbs(codex_home, thread_id, snapshot)?;
Ok(restored)
}
pub fn validate_thread_sidebar_snapshot(
codex_home: &Path,
snapshot: &Value,
) -> anyhow::Result<()> {
let thread_id = snapshot
.get("thread_id")
.and_then(Value::as_str)
.filter(|id| !id.trim().is_empty())
.ok_or_else(|| anyhow::anyhow!("sidebar snapshot is missing thread_id"))?;
if let Some(catalog) = snapshot.get("catalog") {
let entries = catalog
.as_array()
.ok_or_else(|| anyhow::anyhow!("sidebar snapshot catalog must be an array"))?;
let allowed_paths = sidebar_catalog_db_paths(codex_home)?;
for entry in entries {
let table = entry
.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");
}View on GitHub (pinned to b1ed92e5e4)