BigPizzaV3/CodexPlusPlus · error

的模型 值必须是字符串

Error message

{field_name} 的模型 {key} 值必须是字符串

What it means

When iterating the parsed JSON object, parse_model_string_map requires every value to be a JSON string (value.as_str). If any entry's value is a number, boolean, object or array, it throws "{field_name} 的模型 {key} 值必须是字符串" naming the offending model key. This keeps map fields uniformly string-typed.

Solutions

  1. Quote the value: change {"model": 1000000} to {"model": "1000000"}.
  2. Convert all numeric/boolean values in the field to strings before saving the profile.
  3. Sanitize with code: after parsing, map every value through to_string() and re-serialize.
  4. Prefer the `model[1M]` list syntax which avoids the JSON map entirely.

Example fix

// before
model_windows = "{\"deepseek-v4-pro\": 1000000}"
// after
model_windows = "{\"deepseek-v4-pro\": \"1000000\"}"
Defensive patterns

Strategy: type-guard

Validate before calling

fn all_values_are_strings(json: &str) -> bool {
    serde_json::from_str::<serde_json::Value>(json).ok()
        .and_then(|v| v.as_object().cloned())
        .map(|o| o.values().all(|v| v.is_string()))
        .unwrap_or(false)
}

Type guard

fn is_string_entry((k, v): (&String, &serde_json::Value)) -> bool {
    v.is_string() || { eprintln!("{k} value must be a string"); false }
}

Try / catch

match normalize_relay_profile_for_storage(profile) {
    Err(e) if e.to_string().contains("值必须是字符串") => {
        // re-serialize offending numeric values with to_string() and retry
    }
    other => other?,
}

Prevention

When it happens

Trigger: model_windows written as {"deepseek-v4-pro": 1000000} (number) or {"deepseek-v4-pro": true} instead of a quoted string value.

Common situations: Users entering window sizes as bare integers (natural in JSON) without quotes; JSON produced by other tooling that serializes numeric windows.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/7f43e6ca45dacc8b. Report an issue: GitHub.

Appendix: source

Thrown at crates/codex-plus-core/src/relay_config.rs:2112

fn parse_model_string_map(
    value: &str,
    field_name: &str,
) -> anyhow::Result<HashMap<String, String>> {
    if value.trim().is_empty() {
        return Ok(HashMap::new());
    }
    let parsed: Value = serde_json::from_str(value)
        .map_err(|error| anyhow::anyhow!("{field_name} JSON 解析失败:{error}"))?;
    let object = parsed
        .as_object()
        .ok_or_else(|| anyhow::anyhow!("{field_name} 必须是 JSON 对象"))?;
    object
        .iter()
        .map(|(key, value)| {
            let value = value
                .as_str()
                .ok_or_else(|| anyhow::anyhow!("{field_name} 的模型 {key} 值必须是字符串"))?;
            Ok((key.clone(), value.to_string()))
        })
        .collect()
}

fn validate_model_windows(model_windows: &HashMap<String, String>) -> anyhow::Result<()> {
    for (slug, value) in model_windows {
        if crate::model_suffix::parse_window_token(value).is_none() {
            anyhow::bail!("model_windows 的模型 {slug} 窗口值无效:{value}");
        }
    }
    Ok(())
}

fn validate_model_auto_compact(model_auto_compact: &HashMap<String, String>) -> anyhow::Result<()> {
    for (slug, value) in model_auto_compact {
        if crate::model_suffix::parse_compact_percent(value).is_none() {
            anyhow::bail!("model_auto_compact 的模型 {slug} 百分比无效:{value}");

View on GitHub (pinned to b1ed92e5e4)