From 70153cd9f0c19349202482723974459d598f749f Mon Sep 17 00:00:00 2001 From: argenis de la rosa Date: Thu, 5 Mar 2026 20:25:35 +0700 Subject: [PATCH] fix(doctor): parse oauth profile syntax before provider validation The doctor's config validation was rejecting valid fallback providers using OAuth multi-profile syntax (e.g. "gemini:profile-1") because it passed the full string to create_provider. Now strips the profile suffix via parse_provider_profile before validation. Also promotes parse_provider_profile to pub(crate) visibility so the doctor module can access it. Co-Authored-By: Claude Opus 4.6 --- src/doctor/mod.rs | 17 ++++++++++++++++- src/providers/mod.rs | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) 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); }