Hmbown/CodeWhale · error
OAuth device-code request returned success without a device…
Error message
OAuth device-code request returned success without a device and user code
What it means
Thrown when the device-code endpoint responds with HTTP success and no `error` field, but the response body lacks a non-empty `device_code` or `user_code`. This is a response-shape invariant: a 200 without the required grant fields means the provider's device flow is broken or the response was not actually a device grant.
Solutions
- Verify the provider's device-authorization endpoint is correct (check discovery metadata / `device_code_path` config)
- Check the provider's API changelog for renamed device-grant response fields
- Capture the raw response body (proxy/logging) to confirm what the provider actually returned
- Fall back to browser (PKCE) sign-in: `codewhale` login without the device flow
Defensive patterns
Strategy: validation
Validate before calling
// validate the grant response before using it
if (!body.device_code || !body.device_code.trim() || !body.user_code || !body.user_code.trim()) {
throw new Error("device grant response missing device_code/user_code");
} Type guard
function isValidDeviceGrant(b) {
return typeof b?.device_code === 'string' && b.device_code.trim() !== '' &&
typeof b?.user_code === 'string' && b.user_code.trim() !== '';
} Prevention
- Pin/verify the provider's discovery metadata points at the real device_authorization_endpoint
- Log raw response bodies for OAuth endpoints in debug mode
- Watch provider API changelogs for device-flow response changes
When it happens
Trigger: `request_device_grant` receives a 2xx response whose parsed body has empty, whitespace-only, or missing `device_code`/`user_code` fields — e.g. the discovery resolved to the token endpoint instead of the device-authorization endpoint, or the provider returned an empty JSON body.
Common situations: Misconfigured provider metadata (wrong `device_code_path` or a discovery document advertising a device endpoint that returns something else); provider API version change altering field names; a proxy stripping or truncating the JSON body.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- agy OAuth token JSON carries no access token member
- agy OAuth token member
- atomically replacing xAI OAuth credentials
- bearer credentials are not an API key
- ChatGPT revoke task was lost
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/7853529b29fa901f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/oauth.rs:754
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(
token_endpoint: &str,
client_id: &str,
device_code: &str,
) -> Result<codewhale_config::device_code::DevicePollOutcome<OAuthTokenMaterial>> {
use codewhale_config::device_code::DevicePollOutcome;
let token_endpoint = oauth_endpoint_url(token_endpoint)?;
let client = oauth_http_client("device-code poll")?;
let params = [
("client_id", client_id),
("grant_type", "urn:ietf:params:oauth:grant-type:device_code"),
("device_code", device_code),
];
#[cfg(test)]View on GitHub (pinned to 73e0f67d83)