Hmbown/CodeWhale · warning

offers no device-code flow; sign in through the browser…

Error message

{} offers no device-code flow; sign in through the browser login instead

What it means

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.

Solutions

  1. Use the browser/PKCE sign-in flow for this provider instead (`{provider} login` without the device option)
  2. Check `oauth_provider_params` to confirm which flows the provider supports
  3. If device flow is expected, verify you are targeting a provider build/config that defines `device_code_path`

Example fix

// before
let pending = device_code_login(provider)?; // bails for PKCE-only providers
// after
if supports_device_flow(provider) {
    let pending = device_code_login(provider)?;
} else {
    let pending = pkce_login(provider).await?;
}
Defensive patterns

Strategy: validation

Validate before calling

// check flow support before calling device login
const params = oauthProviderParams(provider);
if (!params.device_code_path) {
  return pkceLogin(provider); // or surface a clear message
}

Type guard

function supportsDeviceFlow(provider) {
  return oauthProviderParams(provider)?.device_code_path != null;
}

Try / catch

try {
  await deviceCodeLogin(provider);
} catch (e) {
  if (String(e).includes('no device-code flow')) {
    await pkceLogin(provider); // fallback to browser login
  } else { throw e; }
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

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


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/05a6cc72a65196a7. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/oauth.rs:810

                body.error_description.as_deref(),
                status,
            );
            bail!("OAuth device-code poll failed ({detail})");
        }
    }
}

/// Interactive device-code login for any provider whose row offers it.
/// Prints the verification URL + user code to stderr and polls until
/// approved. A provider with no device flow (ChatGPT) fails here with the
/// reason, instead of deep in transport code.
pub async fn device_code_login(provider: OAuthProvider) -> Result<PendingOAuthLogin> {
    // Endpoint resolution does blocking HTTP (discovery): it must run on the
    // blocking worker, never on the async executor. Providers with no device
    // flow fail here, before any thread spawns and before any network.
    let params = oauth_provider_params(provider);
    if params.device_code_path.is_none() {
        bail!(
            "{} offers no device-code flow; sign in through the browser login instead",
            params.display_name
        );
    }
    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> {

View on GitHub (pinned to 73e0f67d83)