{"record":{"id":"05a6cc72a65196a7","repo":"Hmbown/CodeWhale","slug":"offers-no-device-code-flow-sign-in-through-the-browser-login","errorCode":null,"errorMessage":"{} offers no device-code flow; sign in through the browser login instead","messagePattern":"(.+?) offers no device-code flow; sign in through the browser login instead","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/tui/src/oauth.rs","lineNumber":810,"sourceCode":"                body.error_description.as_deref(),\n                status,\n            );\n            bail!(\"OAuth device-code poll failed ({detail})\");\n        }\n    }\n}\n\n/// Interactive device-code login for any provider whose row offers it.\n/// Prints the verification URL + user code to stderr and polls until\n/// approved. A provider with no device flow (ChatGPT) fails here with the\n/// reason, instead of deep in transport code.\npub async fn device_code_login(provider: OAuthProvider) -> Result<PendingOAuthLogin> {\n    // Endpoint resolution does blocking HTTP (discovery): it must run on the\n    // blocking worker, never on the async executor. Providers with no device\n    // flow fail here, before any thread spawns and before any network.\n    let params = oauth_provider_params(provider);\n    if params.device_code_path.is_none() {\n        bail!(\n            \"{} offers no device-code flow; sign in through the browser login instead\",\n            params.display_name\n        );\n    }\n    let inputs = params.resolve_inputs();\n    let display_name = params.display_name;\n    tokio::task::spawn_blocking(move || device_code_login_with(provider, &inputs))\n        .await\n        .with_context(|| format!(\"{display_name} device-code login worker failed\"))?\n}\n\n/// Blocking worker body for [`device_code_login`]. `pub(crate)` so the\n/// legacy activation tests can drive the unified login end to end until\n/// activation unifies in 3b-ii.\npub(crate) fn device_code_login_with(\n    provider: OAuthProvider,\n    inputs: &ResolvedOAuthInputs,\n) -> Result<PendingOAuthLogin> {","sourceCodeStart":792,"sourceCodeEnd":828,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/oauth.rs#L792-L828","documentation":"Fail-fast guard in `device_code_login` thrown before any network or thread spawn when the provider's static parameters have no `device_code_path`. The provider simply does not implement the RFC 8628 device flow, so device-code sign-in is not possible; the message directs the user to the browser (PKCE) login.","triggerScenarios":"Calling `device_code_login(provider)` for a provider whose `oauth_provider_params` entry has `device_code_path: None` (e.g. a provider that only supports the authorization-code/PKCE flow).","commonSituations":"Selecting the device-code sign-in option for a PKCE-only provider; config or code update removing the device flow for a provider; scripting a login flow that assumes all providers support device grants.","solutions":["Use the browser/PKCE sign-in flow for this provider instead (`{provider} login` without the device option)","Check `oauth_provider_params` to confirm which flows the provider supports","If device flow is expected, verify you are targeting a provider build/config that defines `device_code_path`"],"exampleFix":"// before\nlet pending = device_code_login(provider)?; // bails for PKCE-only providers\n// after\nif supports_device_flow(provider) {\n    let pending = device_code_login(provider)?;\n} else {\n    let pending = pkce_login(provider).await?;\n}","handlingStrategy":"validation","validationCode":"// check flow support before calling device login\nconst params = oauthProviderParams(provider);\nif (!params.device_code_path) {\n  return pkceLogin(provider); // or surface a clear message\n}","typeGuard":"function supportsDeviceFlow(provider) {\n  return oauthProviderParams(provider)?.device_code_path != null;\n}","tryCatchPattern":"try {\n  await deviceCodeLogin(provider);\n} catch (e) {\n  if (String(e).includes('no device-code flow')) {\n    await pkceLogin(provider); // fallback to browser login\n  } else { throw e; }\n}","preventionTips":["Consult the provider capability table before choosing a login flow","Offer users the browser (PKCE) login as the default when device flow is unavailable","Keep provider flow support documented next to the provider list"],"tags":["oauth","unsupported-flow","device-code"],"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"}