{"record":{"id":"897d73bed4b1bd25","repo":"BigPizzaV3/CodexPlusPlus","slug":"catalog-json-error","errorCode":null,"errorMessage":"catalog JSON 解析失败：{error}","messagePattern":"catalog JSON 解析失败：(.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/relay_config.rs","lineNumber":2168,"sourceCode":"    Ok(map.clone())\n}\n\nfn model_metadata_has_entries(\n    metadata: &serde_json::Map<String, Value>,\n    entry_slugs: &HashSet<String>,\n) -> bool {\n    metadata.keys().any(|slug| entry_slugs.contains(slug))\n}\n\nfn apply_model_metadata_overrides(\n    catalog_json: &str,\n    override_map: &serde_json::Map<String, Value>,\n) -> anyhow::Result<String> {\n    if override_map.is_empty() {\n        return Ok(catalog_json.to_string());\n    }\n    let mut catalog: Value = serde_json::from_str(catalog_json)\n        .map_err(|error| anyhow::anyhow!(\"catalog JSON 解析失败：{error}\"))?;\n    let Some(models) = catalog.get_mut(\"models\").and_then(Value::as_array_mut) else {\n        return Ok(catalog_json.to_string());\n    };\n    for model in models {\n        let Some(slug) = model.get(\"slug\").and_then(Value::as_str) else {\n            continue;\n        };\n        let Some(user_override) = override_map.get(slug).and_then(Value::as_object) else {\n            continue;\n        };\n        let Some(model_object) = model.as_object_mut() else {\n            continue;\n        };\n        for (key, value) in user_override {\n            // 窗口与压缩阈值由 model_windows / model_auto_compact 生成（issue #2191）；\n            // max_context_window 是 codex 运行时的 clamp 权威，绝不能被历史 metadata 残留值覆盖。\n            if matches!(\n                key.as_str(),","sourceCodeStart":2150,"sourceCodeEnd":2186,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/relay_config.rs#L2150-L2186","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (catalog.json)\n{\"models\": [{\"slug\": \"a\"}, {\"slug\": \"b\"} // trailing comment\n// after (catalog.json)\n{\"models\": [{\"slug\": \"a\"}, {\"slug\": \"b\"}]}","handlingStrategy":"validation","validationCode":"fn catalog_json_is_valid(catalog: &str) -> bool {\n    serde_json::from_str::<serde_json::Value>(catalog)\n        .map(|v| v.get(\"models\").and_then(|m| m.as_array()).is_some())\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match apply_model_metadata_overrides(catalog, overrides) {\n    Err(e) if e.to_string().contains(\"catalog JSON 解析失败\") => {\n        // regenerate the catalog from model_list instead of using the corrupt string\n    }\n    other => other?,\n}","preventionTips":["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"],"tags":["json","parse-error","catalog"],"backgroundTag":"json-parse-error","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"}