{"record":{"id":"52c6ecfd9455a8d2","repo":"lbjlaq/Antigravity-Manager","slug":"failed-to-parse-model-specs-json","errorCode":null,"errorMessage":"Failed to parse model_specs.json","messagePattern":"Failed to parse model_specs\\.json","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src-tauri/src/proxy/model_specs.rs","lineNumber":21,"sourceCode":"use serde::{Deserialize, Serialize};\nuse std::collections::HashMap;\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ModelSpec {\n    pub max_output_tokens: Option<u64>,\n    pub thinking_budget: Option<u64>,\n    pub is_thinking: Option<bool>,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\nstruct SpecsConfig {\n    models: HashMap<String, ModelSpec>,\n    aliases: HashMap<String, String>,\n}\n\nstatic SPECS: Lazy<SpecsConfig> = Lazy::new(|| {\n    let json_str = include_str!(\"../../resources/model_specs.json\");\n    serde_json::from_str(json_str).expect(\"Failed to parse model_specs.json\")\n});\n\n/// 获取归一化后的模型 ID (基于别名)\npub fn resolve_alias(model_id: &str) -> String {\n    SPECS\n        .aliases\n        .get(model_id)\n        .cloned()\n        .unwrap_or_else(|| model_id.to_string())\n}\n\n/// 获取模型输出 Token 限额 (动态优先)\npub fn get_max_output_tokens(model_id: &str, token: Option<&ProxyToken>) -> u64 {\n    let std_id = resolve_alias(model_id);\n\n    // 1. 尝试从账号动态数据中读取\n    if let Some(t) = token {\n        if let Some(&limit) = t.model_limits.get(&std_id) {","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/lbjlaq/Antigravity-Manager/blob/90a2b6b20797e2e593a83d0c54ab86037863fe70/src-tauri/src/proxy/model_specs.rs#L3-L39","documentation":"The SPECS lazy static embeds resources/model_specs.json at compile time and parses it with expect, panicking at first access if the embedded JSON is invalid. This is a startup-time invariant: the shipped resource must always be parseable.","triggerScenarios":"First access to SPECS (e.g. resolve_alias) after model_specs.json was edited/committed with invalid JSON, duplicate keys breaking serde expectations, or wrong types for ModelSpec fields.","commonSituations":"A hand-edit or CI generation step produced malformed JSON; a ModelSpec field type changed making existing JSON invalid; file re-encoded with BOM breaking serde_json.","solutions":["Validate model_specs.json with serde_json (or a CI lint step) before building — the error is baked in at compile time","Fix the JSON syntax/type errors in resources/model_specs.json","Replace expect with a logged error and empty fallback so one bad file doesn't panic the proxy"],"exampleFix":"// before\nserde_json::from_str(json_str).expect(\"Failed to parse model_specs.json\")\n// after\nserde_json::from_str(json_str).unwrap_or_else(|e| {\n    log_error(\"model_specs.json parse failed: {e}\");\n    Default::default()\n})","handlingStrategy":"validation","validationCode":"let specs: SpecsConfig = serde_json::from_str(&std::fs::read_to_string(\"resources/model_specs.json\")?)?; // CI check before build","typeGuard":null,"tryCatchPattern":"match serde_json::from_str::<SpecsConfig>(json_str) { Ok(v) => v, Err(e) => { log::error(\"model_specs parse: {e}\"); Default::default() } }","preventionTips":["Add a CI job that parses model_specs.json with the same serde types","Reject files with BOM or trailing garbage","Keep the JSON machine-generated to avoid hand-edit syntax errors"],"tags":["rust","json","startup","panic"],"backgroundTag":"json-parse-error","analyzedSha":"90a2b6b20797e2e593a83d0c54ab86037863fe70","analyzedAt":"2026-09-19T08:34:01.322Z","contentChangedAt":"2026-09-19T08:34:01.322Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}