{"record":{"id":"0798c387965f1d20","repo":"BigPizzaV3/CodexPlusPlus","slug":"api-key","errorCode":null,"errorMessage":"API Key 为空","messagePattern":"API Key 为空","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/provider_import.rs","lineNumber":229,"sourceCode":"        sub2api_multiplier: String::new(),\n        model_routes: Vec::new(),\n    }\n}\n\nfn normalize_request(mut request: ProviderImportRequest) -> anyhow::Result<ProviderImportRequest> {\n    request.name = request.name.trim().to_string();\n    request.base_url = request.base_url.trim().trim_end_matches('/').to_string();\n    request.api_key = request.api_key.trim().to_string();\n    request.wire_api = request.wire_api.trim().to_ascii_lowercase();\n    request.relay_mode = request.relay_mode.trim().to_ascii_lowercase();\n    if request.name.is_empty() {\n        anyhow::bail!(\"供应商名称为空\");\n    }\n    if request.base_url.is_empty() {\n        anyhow::bail!(\"Base URL 为空\");\n    }\n    if request.api_key.is_empty() {\n        anyhow::bail!(\"API Key 为空\");\n    }\n    request.config_contents = build_config_toml(\n        &request.base_url,\n        &request.api_key,\n        relay_protocol(&request.wire_api),\n    );\n    request.auth_contents = build_auth_json(&request.api_key);\n    Ok(request)\n}\n\nfn relay_protocol(value: &str) -> RelayProtocol {\n    match value.trim().to_ascii_lowercase().as_str() {\n        \"chat\" | \"chat_completions\" | \"chat-completions\" => RelayProtocol::ChatCompletions,\n        _ => RelayProtocol::Responses,\n    }\n}\n\nfn relay_mode(value: &str) -> RelayMode {","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/1f431ae49b57b3055e0e6845ba6156c6b4232b4d/crates/codex-plus-core/src/provider_import.rs#L211-L247","documentation":"Thrown by normalize_request in crates/codex-plus-core/src/provider_import.rs when the import request's api_key is empty after trimming. The key is written into the generated auth.json (build_auth_json) and config.toml, so a blank key aborts the import. For URL imports this maps to the apiKey query parameter.","triggerScenarios":"Import request with apiKey omitted or whitespace; share link whose apiKey= parameter is empty; user deliberately omitting the key for a keyless gateway — still rejected, since the import path requires one.","commonSituations":"Share links generated before the key was chosen; frontend forms not enforcing the key field; users assuming local gateways need no key.","solutions":["Include a non-empty API key in the import request / apiKey query parameter","If the upstream truly needs no auth, enter any non-empty placeholder and keep in mind the proxy will send it as a bearer token","Regenerate the share link after the key is set"],"exampleFix":"# before\nhttps://app/import?name=VendorAI&baseUrl=https%3A%2F%2Fapi.vendor.com&apiKey=\n\n# after\nhttps://app/import?name=VendorAI&baseUrl=https%3A%2F%2Fapi.vendor.com&apiKey=sk-xxxx","handlingStrategy":"validation","validationCode":"fn api_key_valid(key: &str) -> bool {\n    !key.trim().is_empty()\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Require the apiKey parameter when generating or accepting share links","Mark the key field mandatory in the import UI"],"tags":["provider-import","validation","api-key","required-field"],"backgroundTag":"missing-api-key-config","analyzedSha":"1f431ae49b57b3055e0e6845ba6156c6b4232b4d","analyzedAt":"2026-08-16T20:54:18.598Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}