BigPizzaV3/CodexPlusPlus · error · anyhow::Error

Base URL 不能为空

Error message

Base URL 不能为空

What it means

fetch_relay_profile_model_ids (crates/codex-plus-core/src/model_catalog.rs:564) resolves a ModelSource from a RelayProfile: it prefers upstream_base_url when non-blank, otherwise base_url, both trimmed. If the chosen URL is empty it bails with 'Base URL 不能为空' before any network call. This is config validation: the profile exists but has no usable endpoint URL, so the /models fetch for the model catalog cannot even be addressed.

Source

Thrown at crates/codex-plus-core/src/model_catalog.rs:583

    profile: &RelayProfile,
) -> anyhow::Result<(Vec<String>, String)> {
    let source = ModelSource {
        source_id: format!("relay-profile:{}", profile.id),
        source_type: "relay_profile".to_string(),
        name: if profile.name.trim().is_empty() {
            profile.id.clone()
        } else {
            profile.name.trim().to_string()
        },
        base_url: if profile.upstream_base_url.trim().is_empty() {
            profile.base_url.trim().to_string()
        } else {
            profile.upstream_base_url.trim().to_string()
        },
        api_key: profile.api_key.trim().to_string(),
    };
    if source.base_url.is_empty() {
        anyhow::bail!("Base URL 不能为空");
    }
    let endpoint = models_endpoint(&source.base_url);
    let client = crate::http_client::proxied_client(&profile.user_agent)?;
    let (models, status) = fetch_models_from_source(&client, &source).await;
    if models.is_empty() {
        let message = status
            .get("message")
            .and_then(Value::as_str)
            .unwrap_or("上游没有返回可用模型");
        anyhow::bail!("{message}");
    }
    Ok((models, endpoint))
}

fn preferred_responses_api_status(sources: &[Value]) -> Value {
    let statuses = sources
        .iter()
        .filter_map(|source| source.get("responses_api"))

View on GitHub (pinned to 1f431ae49b)

Solutions

  1. Fill in a base URL: set upstream_base_url (preferred) or base_url on the relay profile before refreshing its model list
  2. Trim and validate before calling: reject blank URLs in the UI/save path so profiles cannot persist without an endpoint
  3. If the profile is a placeholder, remove or disable it so catalog refresh skips it
  4. Check both fields — an empty upstream_base_url silently falls back to base_url, so both must be blank to hit this

Example fix

// before
let profile = RelayProfile { name: "demo".into(), ..Default::default() }; // no URLs
let (models, endpoint) = fetch_relay_profile_model_ids(&profile).await?; // bail

// after
let profile = RelayProfile {
    name: "demo".into(),
    upstream_base_url: "https://api.example.com".into(),
    ..Default::default()
};
let (models, endpoint) = fetch_relay_profile_model_ids(&profile).await?;
Defensive patterns

Strategy: validation

Validate before calling

fn relay_profile_has_base_url(profile: &RelayProfile) -> bool {
    let upstream = profile.upstream_base_url.trim();
    let base = profile.base_url.trim();
    !upstream.is_empty() || !base.is_empty()
}
assert!(relay_profile_has_base_url(&profile));

Type guard

fn usable_base_url(profile: &RelayProfile) -> Option<&str> {
    let upstream = profile.upstream_base_url.trim();
    let base = profile.base_url.trim();
    Some(if upstream.is_empty() { base } else { upstream }).filter(|u| !u.is_empty())
}

Try / catch

match fetch_relay_profile_model_ids(&profile).await {
    Err(e) if e.to_string().contains("Base URL 不能为空") => {
        // config bug: skip profile, surface to user in UI
        mark_profile_invalid(&profile.id, "missing base URL");
    }
    rest => rest?,
}

Prevention

When it happens

Trigger: Calling fetch_relay_profile_model_ids (directly or via the manager's model-list refresh for a relay profile) where the profile's upstream_base_url and base_url are both empty or whitespace-only — e.g. a newly created profile saved before its URL field was filled, or TOML/settings migration wiping the fields.

Common situations: UI flow letting users save a relay profile without a required URL; profiles imported/migrated from older settings versions where the field was named differently; whitespace-only URLs pasted by accident; tests constructing RelayProfile::default()-like values without setting URLs.

Related errors


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