diff --git a/src/doctor/mod.rs b/src/doctor/mod.rs index edb29a2a3..0c372f8a0 100644 --- a/src/doctor/mod.rs +++ b/src/doctor/mod.rs @@ -616,7 +616,10 @@ fn check_config_semantics(config: &Config, items: &mut Vec) { } fn provider_validation_error(name: &str) -> Option { - match crate::providers::create_provider(name, None) { + // Parse OAuth multi-profile syntax before validation + let (provider_name, _profile) = crate::providers::parse_provider_profile(name); + + match crate::providers::create_provider(provider_name, None) { Ok(_) => None, Err(err) => Some( err.to_string() @@ -1143,6 +1146,18 @@ mod tests { assert_eq!(fb_item.unwrap().severity, Severity::Warn); } + #[test] + fn config_validation_accepts_oauth_profiles() { + let mut config = Config::default(); + config.reliability.fallback_providers = vec!["gemini:profile-1".into()]; + let mut items = Vec::new(); + check_config_semantics(&config, &mut items); + // Should NOT warn about valid provider with profile syntax + assert!(!items + .iter() + .any(|i| { i.severity == Severity::Warn && i.message.contains("gemini") })); + } + #[test] fn config_validation_warns_bad_custom_fallback() { let mut config = Config::default(); diff --git a/src/providers/mod.rs b/src/providers/mod.rs index dce3f1b40..2995a4714 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -1362,7 +1362,7 @@ fn create_provider_with_url_and_options( /// delimited profile, or `(original_str, None)` otherwise. Entries starting /// with `custom:` or `anthropic-custom:` are left untouched because the colon /// is part of the URL scheme. -fn parse_provider_profile(s: &str) -> (&str, Option<&str>) { +pub(crate) fn parse_provider_profile(s: &str) -> (&str, Option<&str>) { if s.starts_with("custom:") || s.starts_with("anthropic-custom:") { return (s, None); }