Hmbown/CodeWhale · error · anyhow::Error

invalid Codewhale-owned xAI OAuth generation; expected xai-a

Error message

invalid Codewhale-owned xAI OAuth generation; expected xai-auth-<32 lowercase hex>.json

What it means

Thrown by validate_xai_oauth_generation when a Codewhale-owned xAI OAuth generation file name does not match the exact pattern xai-auth-<32 lowercase hex chars>.json. The hex portion must be exactly 32 characters from 0-9 and a-f (no uppercase), and the .json suffix is required. The strict name makes credential files self-describing and unforgeable by accident.

Source

Thrown at crates/config/src/xai_credentials.rs:75

        || path.file_name().and_then(|name| name.to_str()) != Some(value)
    {
        return false;
    }
    let Some(id) = value
        .strip_prefix(XAI_OAUTH_GENERATION_PREFIX)
        .and_then(|value| value.strip_suffix(XAI_OAUTH_GENERATION_SUFFIX))
    else {
        return false;
    };
    id.len() == 32
        && id
            .bytes()
            .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
}

pub fn validate_xai_oauth_generation(value: &str) -> Result<&str> {
    if !is_valid_xai_oauth_generation(value) {
        bail!(
            "invalid Codewhale-owned xAI OAuth generation; expected xai-auth-<32 lowercase hex>.json"
        );
    }
    Ok(value)
}

pub fn xai_oauth_credentials_dir() -> Result<PathBuf> {
    lexical_absolute_path(&crate::codewhale_home()?.join("credentials"))
}

/// Make an owned path absolute without resolving any filesystem component.
/// Canonicalization is deliberately forbidden here: following an existing
/// `credentials` symlink would erase the lexical Codewhale-owned boundary and
/// turn an external directory into an apparently valid destination.
fn lexical_absolute_path(path: &Path) -> Result<PathBuf> {
    let absolute = if path.is_absolute() {
        path.to_path_buf()
    } else {

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Re-run the codewhale login/xAI OAuth flow so a correctly named generation file is created
  2. Or rename the file to match the pattern: xai-auth- + exactly 32 chars of 0-9a-f + .json (e.g. xai-auth-0123456789abcdef0123456789abcdef.json)
  3. If generating names in code, format 16 random bytes as {:032x} — never a UUID with dashes

Example fix

# before
xai-auth-0123456789ABCDEF0123456789ABCDEF.json  (uppercase hex)
xai-auth-1.json                                  (wrong length)

# after
xai-auth-0123456789abcdef0123456789abcdef.json
Defensive patterns

Strategy: type-guard

Validate before calling

fn valid_generation_name(name: &str) -> bool {
    let Some(id) = name
        .strip_prefix("xai-auth-")
        .and_then(|v| v.strip_suffix(".json"))
    else { return false; };
    id.len() == 32 && id.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
}

assert!(valid_generation_name(&name), "expected xai-auth-<32 lowercase hex>.json");

Type guard

fn is_valid_generation(name: &str) -> bool {
    match name.strip_prefix("xai-auth-").and_then(|v| v.strip_suffix(".json")) {
        Some(id) => id.len() == 32 && id.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b)),
        None => false,
    }
}

Prevention

When it happens

Trigger: Renaming a credential file manually (auth.json, xai-auth-1.json), passing a generation id with uppercase hex or 31/33 chars, or generating names from a UUID without trimming dashes. Callers that accept a user-supplied generation string hit this on the first format deviation.

Common situations: Backup/restore scripts renaming files; hand-copying a token file between machines and 'tidying' the name; code that builds the file name from random bytes without enforcing lowercase hex and length 32.

Related errors


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