Hmbown/CodeWhale · error · anyhow::Error

The Codewhale service returned an invalid device authorizati

Error message

The Codewhale service returned an invalid device authorization response

What it means

The device_code from the device-authorization response must be exactly 43 characters of ASCII alphanumeric, hyphen, or underscore - the shape of a base64url-encoded 32-byte token. This bail fires when the token has a different length or contains characters like '=', '+', or '/', meaning the response is malformed or from an incompatible service version.

Source

Thrown at crates/cli/src/cloud.rs:873

        || bytes[4] != b'-'
        || bytes[9] != b'-'
        || bytes
            .iter()
            .enumerate()
            .any(|(index, byte)| !matches!(index, 4 | 9) && !ALPHABET.contains(byte))
    {
        bail!("The Codewhale service returned an invalid user code");
    }
    Ok(())
}

fn validate_device_code(code: &str) -> Result<()> {
    if code.len() != 43
        || !code
            .bytes()
            .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_'))
    {
        bail!("The Codewhale service returned an invalid device authorization response");
    }
    Ok(())
}

fn validate_api_key(key: &str) -> Result<()> {
    let bytes = key.len();
    if bytes < MIN_API_KEY_BYTES || bytes as u64 > MAX_API_KEY_BYTES {
        bail!("API key must be {MIN_API_KEY_BYTES}-{MAX_API_KEY_BYTES} UTF-8 bytes");
    }
    if key.chars().any(is_ascii_control) {
        bail!("API key contains invalid control characters");
    }
    Ok(())
}

fn validate_label(label: &str) -> Result<String> {
    let label = label.split_whitespace().collect::<Vec<_>>().join(" ");
    if label.is_empty()

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Emit the device_code as unpadded base64url (43 chars for 32 bytes of entropy) from the service
  2. Update the CLI to the version matching the service's device-code encoding
  3. Verify the full response body is received without truncation (check content-length, proxy buffering)
  4. For local testing, generate codes with 43 chars from [A-Za-z0-9_-]

Example fix

// before
device_code: "c29tZS1kZXZpY2UtY29kZS10b2tlbi1wYWRkZWQ="
// after
device_code: "c29tZS1kZXZpY2UtY29kZS10b2tlbi11bnBhZGRlZA"  // 43 chars, [A-Za-z0-9_-]
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_device_code(code: &str) -> bool {
    code.len() == 43
        && code.bytes().all(|b| b.is_ascii_alphanumeric() || matches!(b, b'-' | b'_'))
}

Type guard

fn is_valid_device_code(code: &str) -> bool {
    code.len() == 43
        && code.bytes().all(|b| b.is_ascii_alphanumeric() || matches!(b, b'-' | b'_'))
}

Prevention

When it happens

Trigger: validate_device_code receives a code with standard base64 padding ('...=='), a 64-char hex token, a JWT, or any length other than 43; also truncated codes cut off by header/line-length limits.

Common situations: A backend switching token encodings (hex, padded base64, JWT) without a CLI update; proxies or logs that truncate long tokens; a mock returning an opaque placeholder string.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/f1beae27741d354f. Report an issue: GitHub.