BigPizzaV3/CodexPlusPlus · error
API Key 为空
Error message
API Key 为空
What it means
Thrown by `normalize_request` in provider_import.rs when `api_key` is empty after trimming. The key is required because it is embedded into the generated auth/config contents for the provider; an import without a key would produce a config that cannot authenticate. This is the third of three sequential required-field checks (name, base_url, api_key).
Solutions
- Supply the actual API key in `request.api_key` before importing.
- If the key comes from an env var, check it is set and non-empty (e.g. `std::env::var("MY_KEY")?`) before building the request.
- In UI flows, require the key field (password input) before enabling the import button.
Example fix
// before
let req = ProviderImportRequest { api_key: std::env::var("RELAY_KEY").unwrap_or_default(), .. };
// after
let req = ProviderImportRequest { api_key: std::env::var("RELAY_KEY").expect("RELAY_KEY must be set"), .. }; Defensive patterns
Strategy: validation
Validate before calling
let key = request.api_key.trim();
if key.is_empty() {
return Err(anyhow::anyhow!("API Key 不能为空"));
}
request.api_key = key.to_string(); Type guard
fn has_api_key(req: &ProviderImportRequest) -> bool { !req.api_key.trim().is_empty() } Try / catch
match import_provider_with_store(&store, request) {
Ok(p) => Ok(p),
Err(e) if e.to_string().contains("API Key 为空") => Err(anyhow::anyhow!("请填写 API Key(可从环境变量读取)")),
Err(e) => Err(e),
} Prevention
- Read keys from env/secret stores with explicit error on absence, never unwrap_or_default("").
- Require the key field (password input) before enabling import in the UI.
- Never log or persist the key in plaintext outside the designated store.
When it happens
Trigger: Calling `import_provider_with_store` with an `api_key` of `""` or whitespace-only while name and base_url are valid; bails at provider_import.rs:231.
Common situations: User skipped the key field intending to fill it later; environment variable holding the key was unset so an empty string was interpolated; key was stripped by a sanitizer or secret-scanning step.
Understand the failure class
Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.
Related errors
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/0798c387965f1d20.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/provider_import.rs:231
model_routes: Vec::new(),
standard_openai_protocol: false,
}
}
fn normalize_request(mut request: ProviderImportRequest) -> anyhow::Result<ProviderImportRequest> {
request.name = request.name.trim().to_string();
request.base_url = request.base_url.trim().trim_end_matches('/').to_string();
request.api_key = request.api_key.trim().to_string();
request.wire_api = request.wire_api.trim().to_ascii_lowercase();
request.relay_mode = request.relay_mode.trim().to_ascii_lowercase();
if request.name.is_empty() {
anyhow::bail!("供应商名称为空");
}
if request.base_url.is_empty() {
anyhow::bail!("Base URL 为空");
}
if request.api_key.is_empty() {
anyhow::bail!("API Key 为空");
}
request.config_contents = build_config_toml(
&request.base_url,
&request.api_key,
relay_protocol(&request.wire_api),
);
request.auth_contents = build_auth_json(&request.api_key);
Ok(request)
}
fn relay_protocol(value: &str) -> RelayProtocol {
match value.trim().to_ascii_lowercase().as_str() {
"chat" | "chat_completions" | "chat-completions" => RelayProtocol::ChatCompletions,
_ => RelayProtocol::Responses,
}
}
fn relay_mode(value: &str) -> RelayMode {View on GitHub (pinned to b1ed92e5e4)