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

  1. Verify the provider's device-authorization endpoint is correct (check discovery metadata / `device_code_path` config)
  2. Check the provider's API changelog for renamed device-grant response fields
  3. Capture the raw response body (proxy/logging) to confirm what the provider actually returned
  4. 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

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


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)