lbjlaq/Antigravity-Manager · critical

Failed to parse model_specs.json

Error message

Failed to parse model_specs.json

What it means

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.

Solutions

  1. Validate model_specs.json with serde_json (or a CI lint step) before building — the error is baked in at compile time
  2. Fix the JSON syntax/type errors in resources/model_specs.json
  3. Replace expect with a logged error and empty fallback so one bad file doesn't panic the proxy

Example fix

// before
serde_json::from_str(json_str).expect("Failed to parse model_specs.json")
// after
serde_json::from_str(json_str).unwrap_or_else(|e| {
    log_error("model_specs.json parse failed: {e}");
    Default::default()
})
Defensive patterns

Strategy: validation

Validate before calling

let specs: SpecsConfig = serde_json::from_str(&std::fs::read_to_string("resources/model_specs.json")?)?; // CI check before build

Try / catch

match serde_json::from_str::<SpecsConfig>(json_str) { Ok(v) => v, Err(e) => { log::error("model_specs parse: {e}"); Default::default() } }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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


AI-assisted analysis of lbjlaq/Antigravity-Manager@90a2b6b207 (2026-09-19). Data as JSON: /api/errors/52c6ecfd9455a8d2. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/src/proxy/model_specs.rs:21

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelSpec {
    pub max_output_tokens: Option<u64>,
    pub thinking_budget: Option<u64>,
    pub is_thinking: Option<bool>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct SpecsConfig {
    models: HashMap<String, ModelSpec>,
    aliases: HashMap<String, String>,
}

static SPECS: Lazy<SpecsConfig> = Lazy::new(|| {
    let json_str = include_str!("../../resources/model_specs.json");
    serde_json::from_str(json_str).expect("Failed to parse model_specs.json")
});

/// 获取归一化后的模型 ID (基于别名)
pub fn resolve_alias(model_id: &str) -> String {
    SPECS
        .aliases
        .get(model_id)
        .cloned()
        .unwrap_or_else(|| model_id.to_string())
}

/// 获取模型输出 Token 限额 (动态优先)
pub fn get_max_output_tokens(model_id: &str, token: Option<&ProxyToken>) -> u64 {
    let std_id = resolve_alias(model_id);

    // 1. 尝试从账号动态数据中读取
    if let Some(t) = token {
        if let Some(&limit) = t.model_limits.get(&std_id) {

View on GitHub (pinned to 90a2b6b207)