BigPizzaV3/CodexPlusPlus · error
上游 Key 不能为空
Error message
上游 Key 不能为空
What it means
validate_upstream also requires relay.api_key to be non-empty unless the relay profile opts into no-auth mode via uses_no_auth(). This guarantees every forwarded upstream request carries credentials when the upstream requires them.
Solutions
- Set the 上游 Key (api_key) in the active relay profile
- Enable the profile's no-auth option if the upstream requires no key
- Re-apply the profile and retry the request
Example fix
// before api_key = "" // after api_key = "sk-..."
Defensive patterns
Strategy: validation
Validate before calling
if relay.api_key.trim().is_empty() && !relay.uses_no_auth() { return Err("upstream api key missing"); } Try / catch
match send_proxied_request(...) {
Err(e) if e.to_string().contains("上游 Key 不能为空") => eprintln!("请在当前中转填写上游 Key: {e}"),
other => other?,
} Prevention
- Require api_key (or explicit no-auth) at profile creation
- Use validate_upstream before issuing any proxied request
- Re-verify credentials after profile edits or key rotation
When it happens
Trigger: Any proxy request path that calls validate_upstream while relay.api_key is blank and uses_no_auth() is false.
Common situations: Profile created without pasting an API key; key cleared during edit; reusing a local no-auth profile against a real upstream that requires auth.
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
- Chat Completions 上游 Key 不能为空
- 图片上游 Key 不能为空
- 官方混合 API 不应在 auth.json 中保存 OPENAI_API_KEY。请清理此供应商的…
- API Key 不能为空
- 请在设置中填写有效 API Key
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/4943931c336951ca.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/protocol_proxy.rs:1222
) -> reqwest::RequestBuilder {
let mut builder = client
.post(endpoint)
.header(reqwest::header::CONTENT_TYPE, "application/json");
builder = with_relay_auth(builder, relay);
if is_stream {
builder = builder
.header(reqwest::header::ACCEPT, "text/event-stream")
.header(reqwest::header::CACHE_CONTROL, "no-cache");
}
builder.json(upstream_body)
}
fn validate_upstream(relay: &crate::settings::RelayProfile) -> anyhow::Result<()> {
if relay.base_url.trim().is_empty() {
anyhow::bail!("上游 Base URL 不能为空");
}
if relay.api_key.trim().is_empty() && !relay.uses_no_auth() {
anyhow::bail!("上游 Key 不能为空");
}
Ok(())
}
fn with_relay_auth(
request: reqwest::RequestBuilder,
relay: &crate::settings::RelayProfile,
) -> reqwest::RequestBuilder {
if relay.uses_no_auth() {
request
} else {
request.bearer_auth(relay.api_key.trim())
}
}
fn conversation_id_from_responses_request(body: &Value) -> Option<String> {
for key in ["conversation", "conversation_id", "previous_response_id"] {
if let Some(value) = body.get(key).and_then(Value::as_str) {View on GitHub (pinned to b1ed92e5e4)