BigPizzaV3/CodexPlusPlus · error
纯 API 配置写入后未检测到完整 custom provider,请检查 config.toml 和供应商 API…
Error message
纯 API 配置写入后未检测到完整 custom provider,请检查 config.toml 和供应商 API Key。
What it means
After applying a PureApi-mode relay profile, apply_selected_relay_profile re-checks the live home directory via relay_config_status_from_home. If the written config does not register as a complete custom provider (status.configured == false), the switch is treated as failed rather than reporting a half-applied state.
Solutions
- Open the profile in the manager and fill in the provider API Key (and base_url) before switching
- Inspect the generated ~/.codex/config.toml for a complete [model_providers...] block
- Verify auth.json contains OPENAI_API_KEY (or the provider's env key) after the switch
- If another tool keeps rewriting config.toml, stop it or re-run the switch last
Example fix
// before
profile.relay_mode = PureApi; // api_key empty
// after
profile.relay_mode = PureApi;
profile.api_key = Some("sk-...".into()); // provider key required Defensive patterns
Strategy: validation
Validate before calling
const ok = profile.relay_mode !== 'pureApi' || (profile.apiKey && profile.baseUrl);
if (!ok) throw new Error('Pure API mode requires provider API key and base URL'); Type guard
const pureApiReady = (p) => p.relayMode !== 'pureApi' || Boolean(p.apiKey && p.baseUrl);
Try / catch
try { await switchProfile(profile); } catch (e) { if (String(e).includes('纯 API 配置写入后未检测到完整 custom provider')) { openProfileSettingsDialog(); } else throw e; } Prevention
- Require API key and base URL fields when Pure API mode is selected in the UI
- Verify the generated config.toml provider block after each switch
- Ensure no other tool rewrites config.toml immediately after switching
When it happens
Trigger: Switching to a profile with relay_mode == RelayMode::PureApi where, after writing config.toml (and auth), the resulting config is missing provider essentials — e.g. no custom provider block, missing base_url, or no API key present for the provider.
Common situations: User selected Pure API mode but never entered a provider API Key; provider name/base_url left blank; another tool overwrote config.toml between write and verification; auth.json key injection silently failed leaving no OPENAI_API_KEY.
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
- Ambiguous runtime selection; no cache was modified
- 官方混合 API 不应在 auth.json 中保存 OPENAI_API_KEY。请清理此供应商的…
- 请填写 API 的模型名称
- auth.json 必须是 JSON 对象
- Base URL 不能为空
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/6b2f1088ecac12ff.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/relay_switch.rs:151
settings: &BackendSettings,
) -> anyhow::Result<RelaySwitchResult> {
let relay = settings.active_relay_profile();
let common_config = relay_combined_common_config(settings);
let result = if relay.relay_mode == RelayMode::Official && !relay.official_mix_api_key {
let auth_contents =
(!relay.auth_contents.trim().is_empty()).then_some(relay.auth_contents.as_str());
crate::relay_config::clear_relay_config_to_home_with_auth(home, auth_contents)?
} else {
validate_switch_profile_files(&relay)?;
crate::relay_config::apply_relay_profile_to_home_with_switch_rules(
home,
&relay,
&common_config,
)?
};
let status = relay_config_status_from_home(home);
if relay.relay_mode == RelayMode::PureApi && !status.configured {
anyhow::bail!(
"纯 API 配置写入后未检测到完整 custom provider,请检查 config.toml 和供应商 API Key。"
);
}
Ok(RelaySwitchResult {
settings: settings.clone(),
configured: status.configured,
backup_path: result.backup_path,
})
}
fn validate_switch_profile_files(profile: &crate::settings::RelayProfile) -> anyhow::Result<()> {
if profile.relay_mode != RelayMode::Aggregate && profile.config_contents.trim().is_empty() {
anyhow::bail!(
"供应商「{}」缺少独立 config.toml,已停止切换,避免继续显示上一套配置文件。",
if profile.name.trim().is_empty() {
profile.id.as_str()
} else {
profile.name.as_str()View on GitHub (pinned to b1ed92e5e4)