{"record":{"id":"03da467364641ae7","repo":"BigPizzaV3/CodexPlusPlus","slug":"grok-03da46","errorCode":null,"errorMessage":"供应商「{}」没有配置模型列表，无法应用到 Grok","messagePattern":"供应商「(.+?)」没有配置模型列表，无法应用到 Grok","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/tools/grok.rs","lineNumber":132,"sourceCode":"    }\n}\n\n/// 从磁盘读 Grok 的当前状态。\npub fn load_grok_state() -> anyhow::Result<GrokConfigPayload> {\n    load_grok_config_from_home(&crate::grok_config::default_grok_home_dir())\n}\n\n/// 把一个 profile 写进 Grok 的 `config.toml`。\n///\n/// 注意：这会**替换掉 Grok 里所有受管的 `[model.*]` 表**（保留 `[ui]`、\n/// `[models].web_search` 等未管理字段）。调用方必须在 UI 上把这个后果说清楚。\npub fn apply_profile_to_grok(\n    profile: &RelayProfile,\n    backup_root: &Path,\n) -> anyhow::Result<GrokConfigPayload> {\n    let aliases = parse_grok_model_list(&profile.model_list);\n    if aliases.is_empty() {\n        bail!(\n            \"供应商「{}」没有配置模型列表，无法应用到 Grok\",\n            profile.name\n        );\n    }\n\n    let home = crate::grok_config::default_grok_home_dir();\n    apply_profile_to_grok_at(&home, profile, backup_root)\n}\n\n/// `apply_profile_to_grok` 的显式 home 版本，测试用。\npub fn apply_profile_to_grok_at(\n    home: &Path,\n    profile: &RelayProfile,\n    backup_root: &Path,\n) -> anyhow::Result<GrokConfigPayload> {\n    let aliases = parse_grok_model_list(&profile.model_list);\n    if aliases.is_empty() {\n        bail!(","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/tools/grok.rs#L114-L150","documentation":"apply_profile_to_grok converts a RelayProfile's `model_list` into Grok model aliases and writes a Grok config payload. If parsing yields no aliases — meaning the supplier profile has no configured models — there is nothing to write into the Grok config, so the function bails with the supplier's name in the message rather than producing an empty/broken Grok configuration.","triggerScenarios":"Calling apply_profile_to_grok(profile, backup_root) where `profile.model_list` is empty, whitespace-only, or contains only entries that parse_grok_model_list discards (e.g. comments or malformed lines).","commonSituations":"Creating a new provider profile in the manager UI and clicking 'Apply to Grok' before filling in the model list textarea; importing a profile JSON that lacks model_list; clearing the model list to 'reset' it and then applying.","solutions":["Open the profile in CodexPlusPlus manager and populate the model_list textarea (e.g. `grok-4, grok-4-fast`) before applying to Grok.","Verify the profile actually selected/saved: re-open the profile and confirm model_list is non-empty.","If applying programmatically, ensure profile.model_list is set: `profile.model_list = \"grok-4\".into();` before the call."],"exampleFix":"// before\nlet mut profile = load_profile(\"my-provider\");\napply_profile_to_grok(&profile, &backup_root)?; // panics: empty model_list\n// after\nlet mut profile = load_profile(\"my-provider\");\nprofile.model_list = \"grok-4, grok-4-fast\".into();\napply_profile_to_grok(&profile, &backup_root)?;","handlingStrategy":"validation","validationCode":"if crate::tools::grok::parse_grok_model_list(&profile.model_list).is_empty() {\n    return Err(anyhow::anyhow!(\"profile '{}' needs a model list before applying to Grok\", profile.name));\n}\napply_profile_to_grok(&profile, &backup_root)?;","typeGuard":null,"tryCatchPattern":"match apply_profile_to_grok(&profile, &backup_root) {\n    Ok(payload) => save(payload),\n    Err(e) if e.to_string().contains(\"没有配置模型列表\") => prompt_user_to_edit_model_list(&profile.name),\n    Err(e) => return Err(e),\n}","preventionTips":["Require a non-empty model_list when saving a profile in the manager UI.","Disable the 'Apply to Grok' button until the model list textarea has valid entries.","Validate profiles after import before exposing apply actions."],"tags":["config","model-list","grok","validation"],"backgroundTag":"missing-required-config-field","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"}