BigPizzaV3/CodexPlusPlus · error

上游 Base URL 不能为空

Error message

上游 Base URL 不能为空

What it means

Thrown by validate_upstream in crates/codex-plus-core/src/protocol_proxy.rs when a relay about to serve a proxied request has an empty (or whitespace-only) base_url. The check runs for every candidate in the rotation — the selected relay, aggregate members, probe selections, and model-route targets — so any candidate with a blank base URL aborts the request before it is tried.

Source

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

    api_key: &str,
    is_stream: bool,
    upstream_body: &Value,
) -> reqwest::RequestBuilder {
    let mut builder = client
        .post(endpoint)
        .bearer_auth(api_key)
        .header(reqwest::header::CONTENT_TYPE, "application/json");
    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() {
        anyhow::bail!("上游 Key 不能为空");
    }
    Ok(())
}

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) {
            let value = value.trim();
            if !value.is_empty() {
                return Some(value.to_string());
            }
        }
    }
    None
}

View on GitHub (pinned to 1f431ae49b)

Solutions

  1. Identify which relay failed (the diagnostic log entry protocol_proxy.upstream_request names relayId/relayName) and fill in its base URL
  2. Audit every member of the active aggregate and remove placeholder profiles that have no base_url
  3. Save-time validation in the manager: refuse to add a member whose base_url or api_key is empty
Defensive patterns

Strategy: validation

Validate before calling

fn all_candidates_have_base_url(settings: &BackendSettings) -> bool {
    settings
        .relay_profiles
        .iter()
        .filter(|p| settings
            .active_aggregate_relay_profile()
            .map(|a| a.members.iter().any(|m| m.relay_id == p.id))
            .unwrap_or(p.id == settings.active_relay_id))
        .all(|p| !p.base_url.trim().is_empty())
}

Prevention

When it happens

Trigger: An aggregate member relay profile with an empty base_url is selected by rotation; the single active relay never had base_url set; a model-route target profile lost its base_url after an import or hand edit.

Common situations: Aggregate pools containing a placeholder member added but never configured; settings.json edited manually blanking the field; profiles created by duplication where the URL was cleared.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16). Data as JSON: /api/errors/04b414888a04c2d6. Report an issue: GitHub.