{"record":{"id":"ba74a9080ba03a09","repo":"BigPizzaV3/CodexPlusPlus","slug":"model-auto-compact-slug-value","errorCode":null,"errorMessage":"model_auto_compact 的模型 {slug} 百分比无效：{value}","messagePattern":"model_auto_compact 的模型 (.+?) 百分比无效：(.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/relay_config.rs","lineNumber":2130,"sourceCode":"                .ok_or_else(|| anyhow::anyhow!(\"{field_name} 的模型 {key} 值必须是字符串\"))?;\n            Ok((key.clone(), value.to_string()))\n        })\n        .collect()\n}\n\nfn validate_model_windows(model_windows: &HashMap<String, String>) -> anyhow::Result<()> {\n    for (slug, value) in model_windows {\n        if crate::model_suffix::parse_window_token(value).is_none() {\n            anyhow::bail!(\"model_windows 的模型 {slug} 窗口值无效：{value}\");\n        }\n    }\n    Ok(())\n}\n\nfn validate_model_auto_compact(model_auto_compact: &HashMap<String, String>) -> anyhow::Result<()> {\n    for (slug, value) in model_auto_compact {\n        if crate::model_suffix::parse_compact_percent(value).is_none() {\n            anyhow::bail!(\"model_auto_compact 的模型 {slug} 百分比无效：{value}\");\n        }\n    }\n    Ok(())\n}\n\nfn parse_model_metadata_map(metadata_json: &str) -> anyhow::Result<serde_json::Map<String, Value>> {\n    if metadata_json.trim().is_empty() {\n        return Ok(serde_json::Map::new());\n    }\n    let value: Value = serde_json::from_str(metadata_json)\n        .map_err(|error| anyhow::anyhow!(\"model_metadata JSON 解析失败：{error}\"))?;\n    let map = value\n        .as_object()\n        .ok_or_else(|| anyhow::anyhow!(\"model_metadata 必须是 JSON 对象\"))?;\n    for (slug, metadata) in map {\n        if !metadata.is_object() {\n            anyhow::bail!(\"model_metadata 的模型 {slug} 值必须是对象\");\n        }","sourceCodeStart":2112,"sourceCodeEnd":2148,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/relay_config.rs#L2112-L2148","documentation":"validate_model_auto_compact checks each per-model auto-compact percentage in the model_auto_compact map before it is written into the generated Codex config. parse_compact_percent must accept the value as a valid percentage (e.g. an integer 0-100); if it cannot be parsed, the whole apply is aborted. This guards against writing a malformed compact threshold for a model slug.","triggerScenarios":"Calling apply_relay_profile_to_home_with_switch_rules (or any config-apply path) with a RelayProfile whose model_auto_compact map contains a value that parse_compact_percent rejects — e.g. \"85%\", \"1.5\", \"abc\", \"150\", empty string, or a percentage with surrounding text.","commonSituations":"User types a percent sign or decimal into the per-model auto-compact field in the CodexPlusPlus manager UI; config imported from hand-edited JSON with values like \"0.9\"; a model slug was edited and the value got clobbered.","solutions":["Change the value in model_auto_compact to a plain integer percent the parser accepts (e.g. \"80\" not \"80%\" or \"0.8\")","Verify the exact format parse_compact_percent accepts in crates/codex-plus-core/src/model_suffix.rs and match it","Remove the offending slug entry from model_auto_compact if per-model compaction is not needed","Check for whitespace or invisible characters in the imported config value"],"exampleFix":"// before\n\"model_auto_compact\": { \"deepseek-v4-pro\": \"85%\" }\n// after\n\"model_auto_compact\": { \"deepseek-v4-pro\": \"85\" }","handlingStrategy":"validation","validationCode":"function isValidCompactPercent(v) {\n  const n = Number(v);\n  return Number.isInteger(n) && n >= 0 && n <= 100 && String(v).trim() === String(n);\n}\nfor (const [slug, v] of Object.entries(settings.model_auto_compact ?? {})) {\n  if (!isValidCompactPercent(v)) throw new Error(`invalid compact percent for ${slug}: ${v}`);\n}","typeGuard":"const isPlainIntString = (v) => /^\\d+$/.test(String(v).trim()) && Number(v) >= 0 && Number(v) <= 100;","tryCatchPattern":null,"preventionTips":["Store compact thresholds as plain integer strings without '%'","Clamp/normalize user input in the UI before saving settings","Validate imported configs before applying them"],"tags":["config","validation","rust"],"backgroundTag":"invalid-config-value","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"}