Hmbown/CodeWhale · error
OAuth device-code request failed
Error message
OAuth device-code request failed ({detail}) What it means
Thrown when the OAuth 2.0 Device Authorization Grant request (RFC 8628) returns a non-success HTTP status or an OAuth error payload. The helper `oauth_failure_detail` builds a human-readable detail from the provider's `error`/`error_description` fields, falling back to the HTTP status. This library throws it so callers get a single classified message instead of raw HTTP output.
Solutions
- Check the provider's client_id and scopes configured for this OAuth provider entry
- Use the `detail` in the message: match the provider's `error` code (e.g. invalid_client means wrong client credentials)
- Re-run `codewhale` provider discovery or fix the issuer URL so the correct device-authorization endpoint is resolved
- Retry later if the status is 5xx (provider-side outage)
Example fix
// before (generic failure)
let grant = device_code_login(provider)?;
// after (inspect detail first, fix credentials)
match device_code_login(provider) {
Ok(grant) => grant,
Err(e) if e.to_string().contains("invalid_client") => {
eprintln!("fix client_id for this provider");
return Err(e);
}
Err(e) => return Err(e),
} Defensive patterns
Strategy: try-catch
Validate before calling
// ensure client_id and scopes are set before requesting the grant
if client_id.trim().is_empty() || scopes.trim().is_empty() {
return Err("client_id and scopes must be configured for device login");
} Try / catch
try {
let grant = device_code_login(provider).await?;
} catch (e) {
// message contains provider failure detail (error code or HTTP status)
logError("device grant failed", e);
if (isRetryableStatus(e)) scheduleRetry(); else showReLoginHint(provider);
} Prevention
- Keep provider client_id/scopes configuration validated at startup
- Resolve endpoints from fresh discovery metadata before each login
- Monitor provider status pages; surface 5xx as transient to users
When it happens
Trigger: Calling `request_device_grant` (via `device_code_login`) when the device-authorization endpoint returns status >= 400 or a body containing an `error` field, e.g. `invalid_client`, `slow_down`, or an expired/incorrect client_id.
Common situations: Wrong or stale `client_id` configured for the provider; issuer/discovery resolving to the wrong tenant; provider requiring scopes the client is not allowed; network proxy returning 4xx/5xx; provider outage returning 500 with an error body.
Related errors
- OIDC discovery failed with HTTP
- OAuth callback must be GET
- returned HTTP with content type ; expected JSON
- building bundle fetch client failed
- bundle fetch failed with HTTP status
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/b43355ecc3a68442.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/oauth.rs:741
let device_authorization_endpoint = oauth_endpoint_url(device_authorization_endpoint)?;
let client = oauth_http_client("device-code")?;
let params = [("client_id", client_id), ("scope", scopes)];
#[cfg(test)]
crate::external_credentials::record_oauth_network();
let response = client
.post(device_authorization_endpoint)
.form(¶ms)
.send()
.context("OAuth device-code request failed")?;
let (status, body): (_, DeviceGrantResponse) =
parse_oauth_json(response, "OAuth device-code request")?;
if !status.is_success() || body.error.is_some() {
let detail = oauth_failure_detail(
body.error.as_deref(),
body.error_description.as_deref(),
status,
);
bail!("OAuth device-code request failed ({detail})");
}
if body
.device_code
.as_deref()
.is_some_and(|code| !code.trim().is_empty())
&& body
.user_code
.as_deref()
.is_some_and(|code| !code.trim().is_empty())
{
return Ok(body);
}
bail!("OAuth device-code request returned success without a device and user code");
}
/// Poll the token endpoint once, classifying the RFC 8628 outcome. Matches
/// the legacy per-provider poll so the ported tests pin identical behavior.
fn poll_device_grant(View on GitHub (pinned to 73e0f67d83)