Hmbown/CodeWhale · error
xAI OAuth credentials directory must be absolute
Error message
xAI OAuth credentials directory must be absolute
What it means
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.
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.
Example fix
// before
let store = XaiOAuthCredentialStore::open(Path::new("codewhale/creds"))?;
// after
let dir = std::fs::canonicalize("codewhale/creds")?;
let store = XaiOAuthCredentialStore::open(&dir)?; Defensive patterns
Strategy: validation
Validate before calling
anyhow::ensure!(dir.is_absolute(), "credentials directory must be absolute");
Prevention
- Canonicalize configured paths at config load
- Fail loud at startup for relative credential paths
- Use temp_dir()-based absolute paths in tests
When it happens
Trigger: Calling XaiOAuthCredentialStore::open (or the logout-cleanup and test entry points) with a relative Path such as "codewhale/credentials" or "./creds".
Common situations: 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.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Saved Runtime store path must be absolute
- A pinned task provider requires an explicit model
- agent profile reasoning_effort must be one of: inherit…
- budget baseline_receipt path changed
- Codewhale credentials directory has an unsupported component
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/d8e138b941e75f6b.
Report an issue: GitHub.
Appendix: source
Thrown at crates/config/src/xai_credentials.rs:584
}
fn validate_private_basename(name: &str) -> Result<()> {
let path = Path::new(name);
anyhow::ensure!(
path.components().count() == 1
&& matches!(path.components().next(), Some(Component::Normal(_)))
&& path.file_name().and_then(|value| value.to_str()) == Some(name),
"xAI OAuth private basename must be one UTF-8 path component"
);
Ok(())
}
#[cfg(unix)]
fn open_owned_credentials_directory(directory: &Path) -> Result<XaiOAuthCredentialStore> {
use std::os::fd::FromRawFd as _;
use std::os::unix::fs::{MetadataExt as _, PermissionsExt as _};
anyhow::ensure!(
directory.is_absolute(),
"xAI OAuth credentials directory must be absolute"
);
// SAFETY: the literal root path contains no interior NUL and the returned
// descriptor is immediately owned by `File`.
let root_fd = unsafe {
libc::open(
c"/".as_ptr(),
libc::O_RDONLY | libc::O_DIRECTORY | libc::O_CLOEXEC | libc::O_NOFOLLOW,
)
};
if root_fd < 0 {
return Err(std::io::Error::last_os_error()).context("opening filesystem root");
}
// SAFETY: `root_fd` is a newly owned descriptor on the success path above.
let mut current = unsafe { File::from_raw_fd(root_fd) };
for component in directory.components() {
let Component::Normal(name) = component else {View on GitHub (pinned to 73e0f67d83)