{"record":{"id":"d8e138b941e75f6b","repo":"Hmbown/CodeWhale","slug":"xai-oauth-credentials-directory-must-be-absolute","errorCode":null,"errorMessage":"xAI OAuth credentials directory must be absolute","messagePattern":"xAI OAuth credentials directory must be absolute","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/config/src/xai_credentials.rs","lineNumber":454,"sourceCode":"}\n\nfn validate_private_basename(name: &str) -> Result<()> {\n    let path = Path::new(name);\n    anyhow::ensure!(\n        path.components().count() == 1\n            && matches!(path.components().next(), Some(Component::Normal(_)))\n            && path.file_name().and_then(|value| value.to_str()) == Some(name),\n        \"xAI OAuth private basename must be one UTF-8 path component\"\n    );\n    Ok(())\n}\n\n#[cfg(unix)]\nfn open_owned_credentials_directory(directory: &Path) -> Result<XaiOAuthCredentialStore> {\n    use std::os::fd::FromRawFd as _;\n    use std::os::unix::fs::{MetadataExt as _, PermissionsExt as _};\n\n    anyhow::ensure!(\n        directory.is_absolute(),\n        \"xAI OAuth credentials directory must be absolute\"\n    );\n    // SAFETY: the literal root path contains no interior NUL and the returned\n    // descriptor is immediately owned by `File`.\n    let root_fd = unsafe {\n        libc::open(\n            c\"/\".as_ptr(),\n            libc::O_RDONLY | libc::O_DIRECTORY | libc::O_CLOEXEC | libc::O_NOFOLLOW,\n        )\n    };\n    if root_fd < 0 {\n        return Err(std::io::Error::last_os_error()).context(\"opening filesystem root\");\n    }\n    // SAFETY: `root_fd` is a newly owned descriptor on the success path above.\n    let mut current = unsafe { File::from_raw_fd(root_fd) };\n    for component in directory.components() {\n        let Component::Normal(name) = component else {","sourceCodeStart":436,"sourceCodeEnd":472,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/config/src/xai_credentials.rs#L436-L472","documentation":"The unix open_owned_credentials_directory requires an absolute directory because it walks every component with openat(2) starting from '/'. The public path (xai_oauth_credentials_dir -> lexical_absolute_path) always absolutizes first, so this fires only when the opener is called directly with a relative path (in-crate tests or refactors that bypass XaiOAuthCredentialStore::open).","triggerScenarios":"Direct calls such as open_owned_credentials_directory(Path::new(\"credentials\")) from unit tests or new code; a refactor that skips lexical_absolute_path when computing the directory.","commonSituations":"Crate-internal tests constructing stores from relative tempdir paths; refactoring that calls the opener directly instead of through the public open().","solutions":["Join the path against std::env::current_dir() or codewhale_config::codewhale_home() before opening","Use the public entry points (XaiOAuthCredentialStore::open / with_xai_oauth_lifecycle_lock), which absolutize for you"],"exampleFix":"// before\nopen_owned_credentials_directory(Path::new(\".codewhale/credentials\"))?;\n\n// after\nlet dir = codewhale_config::codewhale_home()?.join(\"credentials\");\nassert!(dir.is_absolute());\nopen_owned_credentials_directory(&dir)?;","handlingStrategy":"validation","validationCode":"anyhow::ensure!(\n    dir.is_absolute(),\n    \"credentials directory must be absolute, got {}\",\n    dir.display()\n);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build store paths only through codewhale_home().join(\"credentials\")","In tests, canonicalize tempdirs before constructing stores","Never pass cwd-relative paths to directory-pinning helpers"],"tags":["rust","unix","validation","filesystem","xai-oauth"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}