{"record":{"id":"6b2f1088ecac12ff","repo":"BigPizzaV3/CodexPlusPlus","slug":"api-custom-provider-config-toml","errorCode":null,"errorMessage":"纯 API 配置写入后未检测到完整 custom provider，请检查 config.toml 和供应商 API Key。","messagePattern":"纯 API 配置写入后未检测到完整 custom provider，请检查 config\\.toml 和供应商 API Key。","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/relay_switch.rs","lineNumber":151,"sourceCode":"    settings: &BackendSettings,\n) -> anyhow::Result<RelaySwitchResult> {\n    let relay = settings.active_relay_profile();\n    let common_config = relay_combined_common_config(settings);\n    let result = if relay.relay_mode == RelayMode::Official && !relay.official_mix_api_key {\n        let auth_contents =\n            (!relay.auth_contents.trim().is_empty()).then_some(relay.auth_contents.as_str());\n        crate::relay_config::clear_relay_config_to_home_with_auth(home, auth_contents)?\n    } else {\n        validate_switch_profile_files(&relay)?;\n        crate::relay_config::apply_relay_profile_to_home_with_switch_rules(\n            home,\n            &relay,\n            &common_config,\n        )?\n    };\n    let status = relay_config_status_from_home(home);\n    if relay.relay_mode == RelayMode::PureApi && !status.configured {\n        anyhow::bail!(\n            \"纯 API 配置写入后未检测到完整 custom provider，请检查 config.toml 和供应商 API Key。\"\n        );\n    }\n    Ok(RelaySwitchResult {\n        settings: settings.clone(),\n        configured: status.configured,\n        backup_path: result.backup_path,\n    })\n}\n\nfn validate_switch_profile_files(profile: &crate::settings::RelayProfile) -> anyhow::Result<()> {\n    if profile.relay_mode != RelayMode::Aggregate && profile.config_contents.trim().is_empty() {\n        anyhow::bail!(\n            \"供应商「{}」缺少独立 config.toml，已停止切换，避免继续显示上一套配置文件。\",\n            if profile.name.trim().is_empty() {\n                profile.id.as_str()\n            } else {\n                profile.name.as_str()","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/relay_switch.rs#L133-L169","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nprofile.relay_mode = PureApi; // api_key empty\n// after\nprofile.relay_mode = PureApi;\nprofile.api_key = Some(\"sk-...\".into()); // provider key required","handlingStrategy":"validation","validationCode":"const ok = profile.relay_mode !== 'pureApi' || (profile.apiKey && profile.baseUrl);\nif (!ok) throw new Error('Pure API mode requires provider API key and base URL');","typeGuard":"const pureApiReady = (p) => p.relayMode !== 'pureApi' || Boolean(p.apiKey && p.baseUrl);","tryCatchPattern":"try { await switchProfile(profile); } catch (e) { if (String(e).includes('纯 API 配置写入后未检测到完整 custom provider')) { openProfileSettingsDialog(); } else throw e; }","preventionTips":["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"],"tags":["config","provider","verification"],"backgroundTag":"missing-api-key","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}