Hmbown/CodeWhale · error
Codewhale credentials path must be a directory
Error message
Codewhale credentials path must be a directory
What it means
After opening the directory, the store stats it and requires the resulting metadata to be a directory. If the path exists but is a regular file, symlink-to-file, or other non-directory, opening the credential store is refused because all operations rely on directory-relative file descriptors.
Solutions
- Point the configuration at the parent directory, not the token file.
- Recreate the directory (mkdir -p) and restore or re-authenticate credentials.
- Remove a stale symlink or file occupying the path and recreate it as a directory.
- Canonicalize and stat the path in your own setup code before calling open.
Example fix
// before
let dir = Path::new("~/.codewhale/xai.oauth.v1.json"); // a file
let store = XaiOAuthCredentialStore::open(dir)?;
// after
let dir = Path::new("/home/me/.codewhale");
anyhow::ensure!(dir.metadata()?.is_dir(), "expected directory");
let store = XaiOAuthCredentialStore::open(dir)?; Defensive patterns
Strategy: validation
Validate before calling
anyhow::ensure!(std::fs::metadata(dir).map(|m| m.is_dir()).unwrap_or(false), "credentials path must be a directory");
Prevention
- Configure the parent directory, never the token file path
- mkdir the directory as the runtime user before first run
- Recheck the path after restore/backup operations
When it happens
Trigger: Calling open (or the cleanup/test entry points) with a directory path that actually points to a regular file — e.g. the config path points at a file that replaced a removed directory.
Common situations: A backup/restore replaced the directory with a tar file; the credentials path env var accidentally set to the token file itself rather than its parent directory; a stale symlink now resolving to a file.
Related errors
- A pinned task provider requires an explicit model
- agent profile reasoning_effort must be one of: inherit…
- approval log has no parent
- Automation lock must be a regular file
- cloud job id must look like cloud_
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/380bc03e7241bf00.
Report an issue: GitHub.
Appendix: source
Thrown at crates/config/src/xai_credentials.rs:667
}
if fd < 0 {
return Err(std::io::Error::last_os_error()).with_context(|| {
format!(
"opening Codewhale credentials directory without following links: {}",
crate::quote_os_path(directory)
)
});
}
// SAFETY: `fd` is a newly owned descriptor on the success path above.
current = unsafe { File::from_raw_fd(fd) };
}
let metadata = current.metadata().with_context(|| {
format!(
"inspecting Codewhale credentials directory {}",
crate::quote_os_path(directory)
)
})?;
anyhow::ensure!(
metadata.is_dir(),
"Codewhale credentials path must be a directory"
);
// SAFETY: geteuid(2) dereferences no pointers.
anyhow::ensure!(
metadata.uid() == unsafe { libc::geteuid() },
"Codewhale credentials directory must be owned by the current user"
);
current
.set_permissions(fs::Permissions::from_mode(0o700))
.with_context(|| {
format!(
"securing Codewhale credentials directory {}",
crate::quote_os_path(directory)
)
})?;
Ok(XaiOAuthCredentialStore {
directory: directory.to_path_buf(),View on GitHub (pinned to 73e0f67d83)