BigPizzaV3/CodexPlusPlus · error

Chat Completions 上游 Base URL 不能为空

Error message

Chat Completions 上游 Base URL 不能为空

What it means

After confirming the profile protocol is ChatCompletions, the proxy validates that relay.base_url (the Chat Completions upstream endpoint) is non-empty. An empty URL makes it impossible to forward the request, so it bails before parsing the body.

Solutions

  1. Set the Chat Completions 上游 Base URL (base_url) in the active relay profile to a valid https:// endpoint
  2. Check the settings file for a missing/blank base_url on the active profile
  3. Re-apply the profile after fixing

Example fix

// before
base_url = ""
// after
base_url = "https://api.openai.com"
Defensive patterns

Strategy: validation

Validate before calling

if relay.base_url.trim().is_empty() { return Err("chat completions upstream base url is empty"); }

Try / catch

match proxy_chat_completions(...) {
    Err(e) if e.to_string().contains("Base URL 不能为空") => eprintln!("请填写 Chat Completions 上游 Base URL: {e}"),
    other => other?,
}

Prevention

When it happens

Trigger: Chat Completions proxy request arrives while active_relay_profile().base_url is empty or whitespace after trim.

Common situations: Profile created with protocol=chat_completions but no upstream URL filled in; base_url cleared during profile editing; settings file missing the base_url key.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/f188646ecbdf69ae. Report an issue: GitHub.

Appendix: source

Thrown at crates/codex-plus-core/src/protocol_proxy.rs:1068

fn response_header_timeout(is_stream: bool) -> Duration {
    if is_stream {
        UPSTREAM_STREAM_HEADER_TIMEOUT
    } else {
        UPSTREAM_HEADER_TIMEOUT
    }
}

pub async fn open_chat_completions_proxy_request(
    body: &str,
    original_user_agent: Option<&str>,
) -> anyhow::Result<UpstreamProxyResponse> {
    let settings = SettingsStore::default().load().unwrap_or_default();
    let relay = settings.active_relay_profile();
    if relay.protocol != RelayProtocol::ChatCompletions {
        anyhow::bail!("当前中转未启用 Chat Completions 协议代理");
    }
    if relay.base_url.trim().is_empty() {
        anyhow::bail!("Chat Completions 上游 Base URL 不能为空");
    }
    if relay.api_key.trim().is_empty() && !relay.uses_no_auth() {
        anyhow::bail!("Chat Completions 上游 Key 不能为空");
    }

    let request_json: Value = serde_json::from_str(body)?;
    let is_stream = request_json
        .get("stream")
        .and_then(Value::as_bool)
        .unwrap_or(false);
    let request = crate::http_client::proxied_client(&effective_user_agent(
        &relay.user_agent,
        original_user_agent,
    ))?
    .post(chat_completions_url(&relay.base_url))
    .header(reqwest::header::CONTENT_TYPE, "application/json")
    .json(&request_json);
    let upstream = with_relay_auth(request, &relay).send().await?;

View on GitHub (pinned to b1ed92e5e4)