BigPizzaV3/CodexPlusPlus · error · anyhow::Error
官方混合 API 不应在 auth.json 中保存 OPENAI_API_KEY。请清理此供应商的 auth.json
Error message
官方混合 API 不应在 auth.json 中保存 OPENAI_API_KEY。请清理此供应商的 auth.json 后再切换。
What it means
Thrown by validate_switch_profile_files during apply_selected_relay_profile (called from switch_relay_profile_in_home). It fires when a provider profile with relay_mode == Official and official_mix_api_key == true carries an auth_contents JSON object whose OPENAI_API_KEY is a non-empty string. The official 'hybrid' mode is expected to authenticate with ChatGPT OAuth tokens, not an API key, so the switch is aborted before any config.toml or auth.json is written to keep the previous provider active.
Source
Thrown at crates/codex-plus-core/src/relay_switch.rs:190
profile.id.as_str()
} else {
profile.name.as_str()
}
);
}
if profile.relay_mode == RelayMode::Official
&& serde_json::from_str::<serde_json::Value>(&profile.auth_contents)
.ok()
.and_then(|value| {
value
.get("OPENAI_API_KEY")
.and_then(serde_json::Value::as_str)
.map(str::trim)
.map(str::is_empty)
})
== Some(false)
{
anyhow::bail!(
"官方混合 API 不应在 auth.json 中保存 OPENAI_API_KEY。请清理此供应商的 auth.json 后再切换。"
);
}
Ok(())
}
fn relay_combined_common_config(settings: &BackendSettings) -> String {
let sections = [
settings.relay_common_config_contents.trim(),
settings.relay_context_config_contents.trim(),
]
.into_iter()
.filter(|section| !section.is_empty())
.collect::<Vec<_>>();
if sections.is_empty() {
String::new()
} else {
crate::relay_config::normalize_config_text(&format!("{}\n", sections.join("\n\n")))View on GitHub (pinned to 1f431ae49b)
Solutions
- Open the profile in the manager and remove the OPENAI_API_KEY entry from its auth.json (auth_contents), leaving {} or the OAuth tokens, then switch again
- If the profile really is a Pure API provider, set its relay_mode to PureApi instead of Official
- If you intended key-based official access, set official_mix_api_key = true is already required; the key must live in config.toml (model_provider apiKey), not auth.json — move it there and clear auth_contents
- Programmatically strip the field before switching: parse auth_contents, remove OPENAI_API_KEY, save the profile
Example fix
// before (profile.auth_contents)
{"OPENAI_API_KEY":"sk-abc123"}
// after
{} Defensive patterns
Strategy: validation
Validate before calling
fn can_switch_official_mix(profile: &RelayProfile) -> bool {
if profile.relay_mode != RelayMode::Official || !profile.official_mix_api_key {
return true;
}
serde_json::from_str::<serde_json::Value>(&profile.auth_contents)
.ok()
.and_then(|v| v.get("OPENAI_API_KEY").and_then(Value::as_str))
.map(|k| k.trim().is_empty())
.unwrap_or(true)
}
if !can_switch_official_mix(&profile) {
// surface a form error instead of calling switch_relay_profile_in_home
} Try / catch
match switch_relay_profile_in_home(home, &mut settings) {
Err(e) if e.to_string().contains("OPENAI_API_KEY") => {
// prompt user to clean the profile's auth.json; keep previous provider active
}
other => other?,
} Prevention
- Never let provider_import/ccs_import output feed an Official hybrid profile unchanged — strip OPENAI_API_KEY on import
- When toggling a profile to Official mode in the UI, auto-clear auth_contents or block the toggle until cleaned
- Run the same JSON check in a pre-save validation hook so the profile is never persisted in a state that cannot switch
When it happens
Trigger: Calling switch_relay_profile_in_home (or the bridge '/settings/switch' style flow that reaches apply_selected_relay_profile) for a RelayProfile where relay_mode == RelayMode::Official, official_mix_api_key == true, and serde_json::from_str(profile.auth_contents) yields an object with a string field OPENAI_API_KEY whose trimmed value is non-empty. Note: Official profiles with official_mix_api_key == false never reach this check (they take the clear-config branch at relay_switch.rs:137).
Common situations: Importing a provider from ccs_import/provider_import, which generates auth_contents as {"OPENAI_API_KEY": "sk-..."}, and then flipping that profile to Official hybrid mode; hand-editing the profile's auth.json in the manager UI and pasting an API key; migrating an old Pure API profile to Official mode without clearing the key.
Related errors
- auth.json 必须是 JSON 对象
- 供应商配置总开关已关闭,未写入 config.toml / auth.json。
- Base URL 不能为空
- 中转 Key 不能为空
- config.toml 内容不能为空
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16).
Data as JSON: /api/errors/36848f335303dc81.
Report an issue: GitHub.