{"record":{"id":"4df38db34d186a9e","repo":"BigPizzaV3/CodexPlusPlus","slug":"api-key-4df38d","errorCode":null,"errorMessage":"API Key 不能为空","messagePattern":"API Key 不能为空","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/relay_config.rs","lineNumber":672,"sourceCode":"        config_path: status.config_path,\n        backup_path,\n        configured: status.configured,\n    })\n}\n\npub async fn test_relay_profile(\n    profile: &RelayProfile,\n    model: &str,\n) -> anyhow::Result<RelayProfileTestResult> {\n    let base_url = relay_profile_base_url(profile);\n    let base_url = base_url.trim().trim_end_matches('/');\n    if base_url.is_empty() {\n        anyhow::bail!(\"Base URL 不能为空\");\n    }\n    let api_key = relay_profile_api_key(profile);\n    let api_key = api_key.trim();\n    if api_key.is_empty() && !profile.uses_no_auth() {\n        anyhow::bail!(\"API Key 不能为空\");\n    }\n\n    let client = crate::http_client::proxied_client(\"CodexPlusPlus/RelayTest\")?;\n    let endpoint = match profile.protocol {\n        RelayProtocol::Responses => format!(\"{base_url}/responses\"),\n        RelayProtocol::ChatCompletions => format!(\"{base_url}/chat/completions\"),\n    };\n    let test_model = model.trim();\n    if test_model.is_empty() {\n        anyhow::bail!(\"测试模型不能为空\");\n    }\n\n    let payload = relay_profile_test_payload(profile.protocol, test_model);\n    let mut request = client\n        .post(&endpoint)\n        .header(reqwest::header::CONTENT_TYPE, \"application/json\")\n        .json(&payload);\n    if !profile.uses_no_auth() {","sourceCodeStart":654,"sourceCodeEnd":690,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/relay_config.rs#L654-L690","documentation":"test_relay_profile requires an API key for authentication unless the profile explicitly opts into no-auth mode via uses_no_auth(). If the trimmed key is empty and the profile does not use no-auth, the library bails instead of sending an unauthenticated request that would fail server-side.","triggerScenarios":"Calling test_relay_profile with a profile whose relay_profile_api_key(profile) is empty/whitespace and profile.uses_no_auth() is false.","commonSituations":"User created a profile but forgot to paste the relay key; a key was redacted or stripped when exporting/importing profile settings; user assumes the relay is anonymous but didn't enable the no-auth option.","solutions":["Set the API key on the RelayProfile before testing","If the relay truly needs no auth, mark the profile as no-auth so uses_no_auth() returns true","Pass an explicit key through the test call path if the UI collects one separately"],"exampleFix":"// before\nlet profile = RelayProfile { api_key: \"\", uses_no_auth: false, .. };\n// after\nlet profile = RelayProfile { api_key: \"sk-...\", uses_no_auth: false, .. };\n// or\nlet profile = RelayProfile { api_key: \"\", uses_no_auth: true, .. };","handlingStrategy":"validation","validationCode":"if relay_profile_api_key(profile).trim().is_empty() && !profile.uses_no_auth() {\n    return Err(anyhow::anyhow!(\"API Key 不能为空\"));\n}","typeGuard":null,"tryCatchPattern":"match test_relay_profile(&profile, model) {\n    Err(e) if e.to_string().contains(\"API Key 不能为空\") => open_api_key_dialog(),\n    other => other?,\n}","preventionTips":["Prompt for the key at profile creation","Use the no-auth flag explicitly for anonymous relays","Never strip api_key when serializing profiles locally"],"tags":["validation","authentication","empty-field"],"backgroundTag":"missing-api-key","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"}