{"record":{"id":"8f4b908dcdb94e08","repo":"BigPizzaV3/CodexPlusPlus","slug":"key-8f4b90","errorCode":null,"errorMessage":"中转 Key 不能为空","messagePattern":"中转 Key 不能为空","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/relay_config.rs","lineNumber":405,"sourceCode":"        RelaySessionProvider::Custom,\n    )\n}\n\npub fn apply_relay_config_to_home_with_session_provider(\n    home: &Path,\n    base_url: &str,\n    bearer_token: &str,\n    protocol: RelayProtocol,\n    proxy_port: u16,\n    session_provider: RelaySessionProvider,\n) -> anyhow::Result<RelayApplyResult> {\n    let base_url = base_url.trim();\n    if base_url.is_empty() {\n        anyhow::bail!(\"中转 Base URL 不能为空\");\n    }\n    let bearer_token = bearer_token.trim();\n    if bearer_token.is_empty() {\n        anyhow::bail!(\"中转 Key 不能为空\");\n    }\n    if session_provider == RelaySessionProvider::Openai && protocol != RelayProtocol::Responses {\n        anyhow::bail!(\"OpenAI 会话身份仅支持 Responses API\");\n    }\n    let codex_base_url = codex_base_url_for_protocol(base_url, protocol, proxy_port);\n    let updated = upsert_model_provider_config_with_session_provider(\n        \"\",\n        &codex_base_url,\n        bearer_token,\n        true,\n        session_provider,\n    )?;\n    let auth_contents = serde_json::to_string_pretty(&json!({\n        \"OPENAI_API_KEY\": bearer_token\n    }))?;\n    let backup_path =\n        write_codex_live_atomic(home, Some(&updated), Some(auth_contents.as_bytes()))?;\n    let status = relay_config_status_from_home(home);","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/relay_config.rs#L387-L423","documentation":"Thrown by `apply_relay_config_to_home_with_session_provider` in relay_config.rs when `bearer_token` is empty after trimming. The token becomes the API key written into config.toml/auth for the relay provider; without it codex could not authenticate. The check runs after base_url validation and before the session-provider/protocol compatibility check.","triggerScenarios":"Calling `apply_relay_config_to_home_with_protocol` with a valid base_url but `bearer_token = \"\"` or whitespace; bails at relay_config.rs:405.","commonSituations":"Profile stored without a key (user chose 'fill later'); env var holding the token unset at apply time; token cleared by an edit in the manager UI but apply still invoked.","solutions":["Provide the relay bearer token / API key as the bearer_token argument.","Resolve the key from a secret store or env var and assert it is non-empty before applying.","Gate the apply action in the UI on a non-empty key field."],"exampleFix":"// before\nlet token = std::env::var(\"RELAY_KEY\").unwrap_or_default();\napply_relay_config_to_home_with_protocol(home, url, &token, ...).await?;\n// after\nlet token = std::env::var(\"RELAY_KEY\").context(\"RELAY_KEY not set\")?;\napply_relay_config_to_home_with_protocol(home, url, &token, ...).await?;","handlingStrategy":"validation","validationCode":"let token = bearer_token.trim();\nanyhow::ensure!(!token.is_empty(), \"中转 Key 不能为空\");","typeGuard":"fn has_token(t: &str) -> bool { !t.trim().is_empty() }","tryCatchPattern":"match apply_relay_config_to_home_with_protocol(home, url, key, proto, port, sp).await {\n    Ok(r) => Ok(r),\n    Err(e) if e.to_string().contains(\"中转 Key 不能为空\") => Err(anyhow::anyhow!(\"请先在中转站设置中填写 API Key\")),\n    Err(e) => Err(e),\n}","preventionTips":["Fetch keys from a secret manager with a hard error instead of empty fallback.","Keep key and profile validation together in one pre-apply check function.","In UIs, disable apply while the key field is blank."],"tags":["validation","relay-config","api-key","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"}