Hmbown/CodeWhale · error · io::Error
external credential path must be absolute
Error message
external credential path must be absolute
What it means
`open_secure_regular_file` is the hardened opener used to read external credentials. Before opening anything it requires the path to be absolute; relative paths are rejected up-front with this `InvalidInput` error so the subsequent component-by-component `openat` walk rooted at `/` is well-defined and cannot be influenced by the process's current working directory.
Solutions
- Provide the credential path as an absolute path, e.g. `/home/me/.config/codewhale/credentials.json`.
- Build the path from an explicit base directory (`dirs::home_dir()` or a config root) joined with the relative part, then pass the joined absolute result.
- Check the config/env entry that supplies the path and add the missing leading `/`.
- If the path comes from another tool, print it (`path.is_absolute()`) and see where the relative form originates.
Example fix
// before
let creds = read_to_string("credentials/token")?;
// after
let path = std::path::Path::new(&home).join(".config/codewhale/credentials/token");
let creds = read_to_string(&path)?; // absolute Defensive patterns
Strategy: validation
Validate before calling
if !path.is_absolute() {
return Err(anyhow::anyhow!("credential path must be absolute: {path:?}"));
} Type guard
fn is_absolute_credential_path(p: &Path) -> bool {
p.is_absolute()
} Try / catch
match read_to_string(&cred_path) {
Err(e) if e.to_string().contains("must be absolute") => {
eprintln!("{cred_path:?} is relative; build it from an absolute base dir");
}
other => other?,
} Prevention
- Always construct credential paths from an absolute base (home dir or explicit config root).
- Validate configured paths at startup (fail loud on relative values).
- Avoid string-concatenating paths; use PathBuf::join on an absolute base.
- Document that credential path config values must be absolute.
When it happens
Trigger: Calling `read_to_string`/`read_codewhale_owned_to_string` (-> `open_secure_regular_file`) with a relative path such as `credentials/token` instead of `/home/me/.config/.../credentials/token`.
Common situations: Config files or environment variables holding a credential path written as a relative path; code constructing the path by string concatenation without a leading `/`; moving a working config between machines where the base directory differs.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Codewhale-owned credential file must be singly linked
- Codewhale-owned credential file must be singly linked…
- external credential path contains a NUL byte
- external credential path must be absolute and lexically…
- external credential path must be lexically normalized
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/fd6fc41320c05e4f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/external_credentials.rs:148
);
}
String::from_utf8(bytes).map(Some).with_context(|| {
format!(
"Codewhale-owned credential file {} is not valid UTF-8",
codewhale_config::quote_os_path(path)
)
})
}
#[cfg(unix)]
fn open_secure_regular_file(path: &Path, require_owner_only: bool) -> io::Result<File> {
use std::ffi::CString;
use std::os::fd::FromRawFd;
use std::os::unix::ffi::OsStrExt;
use std::path::Component;
if !path.is_absolute() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"external credential path must be absolute",
));
}
let root = CString::new("/").expect("static root contains no NUL");
// SAFETY: `root` is a valid C string and flags require no variadic mode.
let root_fd = unsafe {
libc::open(
root.as_ptr(),
libc::O_RDONLY | libc::O_DIRECTORY | libc::O_CLOEXEC,
)
};
if root_fd < 0 {
return Err(io::Error::last_os_error());
}
// SAFETY: `root_fd` is newly owned after the successful `open`.
let mut current = unsafe { File::from_raw_fd(root_fd) };View on GitHub (pinned to 73e0f67d83)