Hmbown/CodeWhale · warning
offers no device-code flow; sign in through the browser…
Error message
{display_name} offers no device-code flow; sign in through the browser login instead What it means
The discovery-time twin of the static guard: even though the provider table lists a device flow, the resolved/discovered endpoint metadata contains no `device_authorization_endpoint`. Thrown in the blocking discovery helper before the grant request is made.
Solutions
- Point the issuer at the correct tenant that supports device authorization
- Enable RFC 8628 device flow on your identity provider (e.g. in Keycloak/Auth0/Entra admin settings)
- Refresh the discovery metadata (clear cache / correct well-known URL)
- Fall back to the browser (PKCE) login
Defensive patterns
Strategy: fallback
Validate before calling
// resolve endpoints first and branch
const endpoints = resolveOAuthEndpoints(params, issuer);
if (!endpoints.device_authorization_endpoint) {
return pkceLogin(provider); // discovered metadata lacks device flow
} Try / catch
try {
await startDeviceLogin(provider);
} catch (e) {
if (String(e).includes('no device-code flow')) {
await pkceLogin(provider);
} else { throw e; }
} Prevention
- Verify the issuer tenant has RFC 8628 enabled before advertising device login to users
- Refresh cached discovery documents when endpoints come back empty
- Test login flows against each issuer/tenant you support
When it happens
Trigger: Calling the endpoint-resolution helper when `resolve_oauth_endpoints` returns `device_authorization_endpoint: None` — the issuer's metadata (or configured override) does not advertise a device authorization endpoint.
Common situations: Issuer URL pointing at a tenant/instance that does not support device flow (e.g. some SSO tenants disable it); self-hosted identity provider without RFC 8628 enabled; stale cached discovery document.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- returned an unusable verification URI
- {}
- {message}
- OIDC discovery failed with HTTP
- offers no browser sign-in flow; sign in through the…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/94aac96a55432d3e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/oauth.rs:833
let inputs = params.resolve_inputs();
let display_name = params.display_name;
tokio::task::spawn_blocking(move || device_code_login_with(provider, &inputs))
.await
.with_context(|| format!("{display_name} device-code login worker failed"))?
}
/// Blocking worker body for [`device_code_login`]. `pub(crate)` so the
/// legacy activation tests can drive the unified login end to end until
/// activation unifies in 3b-ii.
pub(crate) fn device_code_login_with(
provider: OAuthProvider,
inputs: &ResolvedOAuthInputs,
) -> Result<PendingOAuthLogin> {
let params = oauth_provider_params(provider);
let display_name = params.display_name;
let endpoints = resolve_oauth_endpoints(params, &inputs.issuer);
let Some(device_endpoint) = endpoints.device_authorization_endpoint else {
bail!(
"{display_name} offers no device-code flow; sign in through the browser login instead"
);
};
let token_endpoint = endpoints.token_endpoint;
let poll_floor_secs = params.device_poll_floor_secs;
let grant = request_device_grant(&device_endpoint, &inputs.client_id, &inputs.scopes)?;
let verify = grant
.verification_uri_complete
.clone()
.or(grant.verification_uri.clone())
.unwrap_or_else(|| format!("{}/device", inputs.issuer.trim_end_matches('/')));
// Off the wire, headed for `webbrowser::open`: must be a bare
// navigation, never a scheme or credential smuggle.
let verify = codewhale_config::device_code::validate_browser_verification_uri(
&verify,
&format!("{display_name} device-code request"),
)?;
let user_code = grant.user_code.unwrap_or_default();View on GitHub (pinned to 73e0f67d83)