{"record":{"id":"65cdcd5c4ccf88f2","repo":"Hmbown/CodeWhale","slug":"offers-no-sign-in-flow","errorCode":null,"errorMessage":"{} offers no sign-in flow","messagePattern":"(.+?) offers no sign-in flow","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/oauth.rs","lineNumber":910,"sourceCode":"    Ok(PendingOAuthLogin {\n        provider,\n        issuer: inputs.issuer.clone(),\n        client_id: inputs.client_id.clone(),\n        token,\n    })\n}\n\n/// One login entry point for every provider: the params row decides whether\n/// the grant is device-code or browser PKCE. A provider with neither fails\n/// here with the reason.\npub async fn login(provider: OAuthProvider) -> Result<PendingOAuthLogin> {\n    let params = oauth_provider_params(provider);\n    if params.device_code_path.is_some() {\n        device_code_login(provider).await\n    } else if params.authorize_path.is_some() {\n        pkce_login(provider).await\n    } else {\n        bail!(\"{} offers no sign-in flow\", params.display_name);\n    }\n}\n\n// ── form-post transport seam ──────────────────────────────────────────\n//\n// One seam for every OAuth form post (PKCE exchange, refresh, revoke): the\n// production client is reqwest with the shared bounds; tests substitute a\n// mock issuer. The seam records network/refresh in test builds so the\n// side-effect trap can still prove \"zero external I/O\" assertions.\n\npub(crate) trait OAuthFormClient {\n    fn post_form(&self, url: &str, form: &[(&str, &str)]) -> Result<(u16, String)>;\n}\n\npub(crate) struct ReqwestOAuthFormClient;\n\nimpl OAuthFormClient for ReqwestOAuthFormClient {\n    fn post_form(&self, url: &str, form: &[(&str, &str)]) -> Result<(u16, String)> {","sourceCodeStart":892,"sourceCodeEnd":928,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/oauth.rs#L892-L928","documentation":"Thrown by the top-level sign-in dispatcher when a provider has neither `device_code_path` nor `authorize_path`, meaning no sign-in flow exists at all for it. This is a configuration/code-table invariant guard rather than a network failure.","triggerScenarios":"Calling the sign-in entry point with a provider whose `oauth_provider_params` row defines neither a device-code path nor a PKCE authorize path (or an unknown/typo'd provider mapping).","commonSituations":"Passing an invalid or newly-added provider name not yet wired into `oauth_provider_params`; provider intentionally login-less (API-key only) being used where OAuth sign-in is requested.","solutions":["Check the provider name you passed against the supported provider list in `oauth_provider_params`","Use the provider's non-OAuth auth method (e.g. API key) if it defines no OAuth flows","Add `device_code_path` or `authorize_path` to the provider row if you maintain this provider definition"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// ensure the provider defines at least one sign-in flow\nconst params = oauthProviderParams(provider);\nif (!params?.device_code_path && !params?.authorize_path) {\n  throw new Error(`${provider} has no OAuth sign-in flow configured`);\n}","typeGuard":"function hasSignInFlow(provider) {\n  const p = oauthProviderParams(provider);\n  return p != null && (p.device_code_path != null || p.authorize_path != null);\n}","tryCatchPattern":"try {\n  await signIn(provider);\n} catch (e) {\n  if (String(e).includes('offers no sign-in flow')) {\n    console.error(`No OAuth flow for ${provider}; use API-key auth instead.`);\n  } else { throw e; }\n}","preventionTips":["Validate provider names against the supported list before invoking sign-in","Register every new provider with at least one auth path in `oauth_provider_params`","Document API-key-only providers so callers do not request OAuth sign-in"],"tags":["oauth","config","unsupported-flow"],"backgroundTag":"unsupported-operation","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}