{"record":{"id":"aa41355fefc55c3d","repo":"Hmbown/CodeWhale","slug":"invalid-codewhale-owned-xai-oauth-generation-expe","errorCode":null,"errorMessage":"invalid Codewhale-owned xAI OAuth generation; expected xai-auth-<32 lowercase hex>.json","messagePattern":"invalid Codewhale-owned xAI OAuth generation; expected xai-auth-<32 lowercase hex>\\.json","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/config/src/xai_credentials.rs","lineNumber":75,"sourceCode":"        || path.file_name().and_then(|name| name.to_str()) != Some(value)\n    {\n        return false;\n    }\n    let Some(id) = value\n        .strip_prefix(XAI_OAUTH_GENERATION_PREFIX)\n        .and_then(|value| value.strip_suffix(XAI_OAUTH_GENERATION_SUFFIX))\n    else {\n        return false;\n    };\n    id.len() == 32\n        && id\n            .bytes()\n            .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))\n}\n\npub fn validate_xai_oauth_generation(value: &str) -> Result<&str> {\n    if !is_valid_xai_oauth_generation(value) {\n        bail!(\n            \"invalid Codewhale-owned xAI OAuth generation; expected xai-auth-<32 lowercase hex>.json\"\n        );\n    }\n    Ok(value)\n}\n\npub fn xai_oauth_credentials_dir() -> Result<PathBuf> {\n    lexical_absolute_path(&crate::codewhale_home()?.join(\"credentials\"))\n}\n\n/// Make an owned path absolute without resolving any filesystem component.\n/// Canonicalization is deliberately forbidden here: following an existing\n/// `credentials` symlink would erase the lexical Codewhale-owned boundary and\n/// turn an external directory into an apparently valid destination.\nfn lexical_absolute_path(path: &Path) -> Result<PathBuf> {\n    let absolute = if path.is_absolute() {\n        path.to_path_buf()\n    } else {","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/config/src/xai_credentials.rs#L57-L93","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-run the codewhale login/xAI OAuth flow so a correctly named generation file is created","Or rename the file to match the pattern: xai-auth- + exactly 32 chars of 0-9a-f + .json (e.g. xai-auth-0123456789abcdef0123456789abcdef.json)","If generating names in code, format 16 random bytes as {:032x} — never a UUID with dashes"],"exampleFix":"# before\nxai-auth-0123456789ABCDEF0123456789ABCDEF.json  (uppercase hex)\nxai-auth-1.json                                  (wrong length)\n\n# after\nxai-auth-0123456789abcdef0123456789abcdef.json","handlingStrategy":"type-guard","validationCode":"fn valid_generation_name(name: &str) -> bool {\n    let Some(id) = name\n        .strip_prefix(\"xai-auth-\")\n        .and_then(|v| v.strip_suffix(\".json\"))\n    else { return false; };\n    id.len() == 32 && id.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))\n}\n\nassert!(valid_generation_name(&name), \"expected xai-auth-<32 lowercase hex>.json\");","typeGuard":"fn is_valid_generation(name: &str) -> bool {\n    match name.strip_prefix(\"xai-auth-\").and_then(|v| v.strip_suffix(\".json\")) {\n        Some(id) => id.len() == 32 && id.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b)),\n        None => false,\n    }\n}","tryCatchPattern":null,"preventionTips":["Never hand-edit generation file names; let the login flow name them","When generating names, format 16 random bytes as {:032x} (lowercase, zero-padded, no dashes)","Add a filename lint to restore scripts: reject anything not matching xai-auth-[0-9a-f]{32}\\.json"],"tags":["rust","oauth","credentials","filename","validation"],"backgroundTag":"invalid-credential-filename","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}