{"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":null,"httpStatus":null,"severity":"error","filePath":"crates/config/src/xai_credentials.rs","lineNumber":584,"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":566,"sourceCodeEnd":602,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/config/src/xai_credentials.rs#L566-L602","documentation":"open_owned_credentials_directory (Unix) only accepts an absolute path for the credentials directory because it opens the directory with openat(2) and pins operations to the fd. Relative paths cannot be safely pinned, so the store refuses them up front.","triggerScenarios":"Calling XaiOAuthCredentialStore::open (or the logout-cleanup and test entry points) with a relative Path such as \"codewhale/credentials\" or \"./creds\".","commonSituations":"Deriving the directory from a relative config value or CWD-dependent string; tests constructing a TempDir path without canonicalization; env var pointing at a relative location.","solutions":["Convert to absolute before calling: std::fs::canonicalize or join against a known root.","Ensure the configured credentials directory in config/env is absolute.","In tests, use temp_dir().join(...) which is already absolute.","Fail loud at config load if the path is relative."],"exampleFix":"// before\nlet store = XaiOAuthCredentialStore::open(Path::new(\"codewhale/creds\"))?;\n// after\nlet dir = std::fs::canonicalize(\"codewhale/creds\")?;\nlet store = XaiOAuthCredentialStore::open(&dir)?;","handlingStrategy":"validation","validationCode":"anyhow::ensure!(dir.is_absolute(), \"credentials directory must be absolute\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Canonicalize configured paths at config load","Fail loud at startup for relative credential paths","Use temp_dir()-based absolute paths in tests"],"tags":["path","configuration","validation"],"backgroundTag":"invalid-config-value","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}