BigPizzaV3/CodexPlusPlus · error

未找到可用的聚合供应商成员

Error message

未找到可用的聚合供应商成员

What it means

Thrown at the end of the failover loop in open_responses_proxy_request_with_settings_and_user_agent (crates/codex-plus-core/src/protocol_proxy.rs:713). The loop iterates the candidate relay list (primary selection plus fallback_relays_after members, or the single model-route target); on the last candidate it always returns, so this bail is reached only when the candidate list is empty — i.e. the aggregate rotation produced no usable member to try. In effect: the active aggregate provider has no members to dispatch the request to.

Source

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

                response: upstream,
            });
        }
        let _ = crate::diagnostic_log::append_diagnostic_log(
            "protocol_proxy.upstream_failover",
            json!({
                "relayId": relay.id,
                "relayName": relay.name,
                "endpoint": endpoint,
                "wireApi": wire_api,
                "stream": is_stream,
                "statusCode": status_code,
                "attempt": attempt + 1,
                "candidateCount": relay_count,
                "headerTimeoutSeconds": header_timeout.as_secs()
            }),
        );
    }
    anyhow::bail!("未找到可用的聚合供应商成员")
}

fn select_model_route(
    settings: &crate::settings::BackendSettings,
    model: &str,
) -> anyhow::Result<Option<ModelRouteSelection>> {
    if model.is_empty() || settings.active_aggregate_relay_profile().is_some() {
        return Ok(None);
    }

    let source = settings.active_relay_profile();
    let Some(route) = source
        .model_routes
        .iter()
        .find(|route| route.model.trim() == model)
    else {
        return Ok(None);
    };

View on GitHub (pinned to 1f431ae49b)

Solutions

  1. Open the aggregate relay profile and add at least one member provider, then save
  2. Verify every member's relay_id in the aggregate still matches an existing relay profile id (deleted members leave dangling ids)
  3. Ensure each member relay has non-empty base_url and api_key so member validation passes
  4. Alternatively switch the active relay away from the empty aggregate to a concrete provider profile
Defensive patterns

Strategy: validation

Validate before calling

fn aggregate_has_usable_members(settings: &BackendSettings) -> bool {
    settings
        .active_aggregate_relay_profile()
        .map(|agg| agg.members.iter().any(|m| {
            settings.relay_profiles.iter().any(|p| {
                p.id == m.relay_id
                    && !p.base_url.trim().is_empty()
                    && !p.api_key.trim().is_empty()
            })
        }))
        .unwrap_or(true)
}

Try / catch

let result = open_responses_proxy_request(body, path).await;
if let Err(e) = &result {
    if e.to_string().contains("聚合供应商成员") {
        // prompt user to configure aggregate members before retrying
    }
}

Prevention

When it happens

Trigger: Active relay is an aggregate whose members list is empty at request time; aggregate members were all deleted or their relay ids no longer resolve so the candidate list collapses to zero; racing settings edits removed members between selection and iteration.

Common situations: User creates an aggregate provider but never adds member relays; members deleted individually while the aggregate stayed active; settings file hand-edited to drop member entries; UI allowed saving an aggregate with zero members.

Related errors


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