{"record":{"id":"b38389da14d3c3ac","repo":"BigPizzaV3/CodexPlusPlus","slug":"grok-config","errorCode":null,"errorMessage":"Grok 模型别名不能为空","messagePattern":"Grok 模型别名不能为空","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/grok_config.rs","lineNumber":277,"sourceCode":"            table.remove(\"api_key\");\n        } else if !input.api_key_update.trim().is_empty() {\n            table.insert(\"api_key\", toml_edit::value(input.api_key_update.trim()));\n        }\n        if models.contains_key(alias) {\n            bail!(\"Grok 模型别名与未管理配置冲突：{alias}\");\n        }\n        models.insert(alias, item);\n    }\n    Ok(())\n}\n\nfn validate_request(request: &SaveGrokConfigRequest) -> anyhow::Result<()> {\n    let mut aliases = HashSet::new();\n    let mut sources = HashSet::new();\n    for input in &request.models {\n        let alias = input.alias.trim();\n        if alias.is_empty() {\n            bail!(\"Grok 模型别名不能为空\");\n        }\n        if alias.chars().any(char::is_control) {\n            bail!(\"Grok 模型别名不能包含控制字符\");\n        }\n        if !aliases.insert(alias.to_string()) {\n            bail!(\"Grok 模型别名重复：{alias}\");\n        }\n        let source = input.source_alias.trim();\n        if !source.is_empty() && !sources.insert(source.to_string()) {\n            bail!(\"Grok 模型来源别名重复：{source}\");\n        }\n        if !matches!(\n            input.api_backend.trim(),\n            \"responses\" | \"chat_completions\" | \"messages\"\n        ) {\n            bail!(\"Grok 模型 {alias} 的 API 协议无效\");\n        }\n        if input.context_window == Some(0) {","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/grok_config.rs#L259-L295","documentation":"validate_request performs input validation on SaveGrokConfigRequest before any file I/O in save_grok_config_at. Every requested model must have a non-empty alias after trimming; otherwise the library bails with \"Grok 模型别名不能为空\". Empty aliases cannot be represented as TOML table keys, so they are rejected up front.","triggerScenarios":"Calling save_grok_config with a request.models element whose alias field is \"\" or whitespace-only (trim() yields empty), triggering the first check in validate_request.","commonSituations":"Frontend form submitted with a blank alias row; programmatic request building that left alias unset; pasting a model entry with a missing name field; whitespace-only input from copy-paste.","solutions":["Inspect each entry in request.models and find the one with an empty/whitespace alias.","Set a non-empty, descriptive alias for that model entry.","Drop the blank entry if it was added accidentally (e.g. an empty form row).","Trim user input in the UI before constructing the request.","Retry the save once all aliases are non-empty and unique."],"exampleFix":"// before\nSaveGrokConfigRequest { models: vec![ModelInput { alias: \"  \".into(), .. }] }\n// after\nSaveGrokConfigRequest { models: vec![ModelInput { alias: \"grok-fast\".into(), .. }] }","handlingStrategy":"validation","validationCode":"fn aliases_non_empty(models: &[ModelInput]) -> bool {\n    models.iter().all(|m| !m.alias.trim().is_empty())\n}","typeGuard":"fn has_alias(m: &ModelInput) -> Option<&str> {\n    let a = m.alias.trim();\n    if a.is_empty() { None } else { Some(a) }\n}","tryCatchPattern":"match save_grok_config(req).await {\n    Err(e) if e.to_string().contains(\"别名不能为空\") => eprintln!(\"a model entry has an empty alias; fill or drop it\"),\n    other => other?,\n}","preventionTips":["Require the alias field in the UI form before submission.","Trim and validate aliases client-side before building the request.","Filter out blank rows from model lists before saving.","Reject whitespace-only aliases in your own input layer."],"tags":["grok-config","input-validation","empty-field"],"backgroundTag":"empty-required-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"}