BigPizzaV3/CodexPlusPlus · error
catalog JSON 解析失败:
Error message
catalog JSON 解析失败:{error} What it means
apply_model_metadata_overrides parses the generated (or provided) catalog JSON before merging per-model metadata overrides. If the catalog string is not valid JSON, it throws "catalog JSON 解析失败:{error}". An empty override_map short-circuits and returns the catalog unchanged, so this only fires when overrides exist and the catalog itself is corrupt.
Solutions
- Fix the JSON syntax at the position reported in the error detail inside the catalog string/file.
- Regenerate the catalog from the model_list suffix syntax (deepseek-v4-pro[1M]) instead of hand-editing.
- Refetch the upstream catalog JSON if it was cached corrupted.
- Verify the catalog parses with serde_json/JSON.parse before passing it into the apply flow.
Example fix
// before (catalog.json)
{"models": [{"slug": "a"}, {"slug": "b"} // trailing comment
// after (catalog.json)
{"models": [{"slug": "a"}, {"slug": "b"}]} Defensive patterns
Strategy: validation
Validate before calling
fn catalog_json_is_valid(catalog: &str) -> bool {
serde_json::from_str::<serde_json::Value>(catalog)
.map(|v| v.get("models").and_then(|m| m.as_array()).is_some())
.unwrap_or(false)
} Try / catch
match apply_model_metadata_overrides(catalog, overrides) {
Err(e) if e.to_string().contains("catalog JSON 解析失败") => {
// regenerate the catalog from model_list instead of using the corrupt string
}
other => other?,
} Prevention
- Regenerate catalogs from the model_list suffix syntax rather than hand-editing
- Atomic-write catalog files (temp file + rename) to avoid truncated JSON
- Verify fetched/cached catalogs parse before persisting
- Keep catalog files out of manual edit paths
When it happens
Trigger: Applying a model catalog where the stored catalog_json (from model_suffix parsing or a fetched /models catalog) is malformed — truncated file, concatenated JSON, or hand-edited catalog content.
Common situations: Manually editing a generated catalog file and breaking syntax, a fetch/cache step writing partial JSON, or passing raw text (not JSON) as catalog_json.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- JSON 解析失败:
- model_metadata JSON 解析失败:
- sidebar catalog entry is missing db_path
- sidebar catalog entry is missing table
- sidebar catalog entry rows must be an array
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/897d73bed4b1bd25.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/relay_config.rs:2168
Ok(map.clone())
}
fn model_metadata_has_entries(
metadata: &serde_json::Map<String, Value>,
entry_slugs: &HashSet<String>,
) -> bool {
metadata.keys().any(|slug| entry_slugs.contains(slug))
}
fn apply_model_metadata_overrides(
catalog_json: &str,
override_map: &serde_json::Map<String, Value>,
) -> anyhow::Result<String> {
if override_map.is_empty() {
return Ok(catalog_json.to_string());
}
let mut catalog: Value = serde_json::from_str(catalog_json)
.map_err(|error| anyhow::anyhow!("catalog JSON 解析失败:{error}"))?;
let Some(models) = catalog.get_mut("models").and_then(Value::as_array_mut) else {
return Ok(catalog_json.to_string());
};
for model in models {
let Some(slug) = model.get("slug").and_then(Value::as_str) else {
continue;
};
let Some(user_override) = override_map.get(slug).and_then(Value::as_object) else {
continue;
};
let Some(model_object) = model.as_object_mut() else {
continue;
};
for (key, value) in user_override {
// 窗口与压缩阈值由 model_windows / model_auto_compact 生成(issue #2191);
// max_context_window 是 codex 运行时的 clamp 权威,绝不能被历史 metadata 残留值覆盖。
if matches!(
key.as_str(),View on GitHub (pinned to b1ed92e5e4)