{"record":{"id":"8450766e126004a8","repo":"Hmbown/CodeWhale","slug":"offers-no-browser-sign-in-flow-sign-in-through-the-device","errorCode":null,"errorMessage":"{} offers no browser sign-in flow; sign in through the device-code login instead","messagePattern":"(.+?) offers no browser sign-in flow; sign in through the device-code login instead","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/oauth.rs","lineNumber":1483,"sourceCode":"        &[\n            (\"grant_type\", \"authorization_code\"),\n            (\"client_id\", client_id),\n            (\"redirect_uri\", redirect_uri),\n            (\"code\", code),\n            (\"code_verifier\", verifier),\n        ],\n    )?;\n    parse_oauth_form_response(status, &body, \"authorization code exchange\", params)\n}\n\n/// Interactive PKCE browser login for any provider whose row offers it.\n/// Prints the authorize URL, opens a browser, and waits for the loopback\n/// callback. A provider with no browser flow (xAI) fails here with the\n/// reason, before any listener binds.\npub async fn pkce_login(provider: OAuthProvider) -> Result<PendingOAuthLogin> {\n    let params = oauth_provider_params(provider);\n    if params.authorize_path.is_none() {\n        bail!(\n            \"{} offers no browser sign-in flow; sign in through the device-code 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 || pkce_login_with(provider, &inputs))\n        .await\n        .with_context(|| format!(\"{display_name} PKCE login worker failed\"))?\n}\n\n/// Blocking worker body for [`pkce_login`]. `pub(crate)` so the activation\n/// tests can drive the unified login end to end until activation unifies.\npub(crate) fn pkce_login_with(\n    provider: OAuthProvider,\n    inputs: &ResolvedOAuthInputs,\n) -> Result<PendingOAuthLogin> {\n    let params = oauth_provider_params(provider);","sourceCodeStart":1465,"sourceCodeEnd":1501,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/oauth.rs#L1465-L1501","documentation":"Entry point for the browser-based PKCE login. Before binding any listener or opening a browser, it checks whether the provider defines an `authorize_path`; providers without a browser flow (xAI) are rejected up front with a message pointing at the device-code alternative.","triggerScenarios":"Calling `pkce_login(provider)` where `oauth_provider_params(provider).authorize_path` is `None` — currently the xAI provider.","commonSituations":"Running the generic browser login command with a device-code-only provider selected; scripting `pkce_login` for xAI; provider registry entry lacking `authorize_path`.","solutions":["Run the device-code login instead: `codewhale auth xai-device`.","Select a provider that supports the browser flow if browser sign-in is what you want.","If adding a new provider, define `authorize_path` in its `OAuthProviderParams` to enable browser login."],"exampleFix":"// before\nlet pending = pkce_login(OAuthProvider::Xai).await?;   // bails\n// after\nlet pending = device_code_login(OAuthProvider::Xai).await?;  // or codewhale auth xai-device","handlingStrategy":"fallback","validationCode":"let params = oauth_provider_params(provider);\nlet use_browser = params.authorize_path.is_some();\nif !use_browser { return device_code_login(provider).await; }","typeGuard":"fn has_browser_login(provider: OAuthProvider) -> bool {\n    oauth_provider_params(provider).authorize_path.is_some()\n}","tryCatchPattern":null,"preventionTips":["Route device-code-only providers (xAI) to their device login command from the start.","Consult `oauth_provider_params` capabilities before presenting a 'Sign in with browser' option.","Define `authorize_path` for any new provider you want on the browser flow."],"tags":["oauth","device-code","unsupported-provider"],"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"}