Hmbown/CodeWhale · error · io::Error
external credential path must name a regular file
Error message
external credential path must name a regular file
What it means
open_secure_regular_file performs TOCTOU-safe hardened opens of credential files. After opening the path it re-checks the metadata of the actually-opened handle and refuses anything that is not a regular file (e.g. a directory, FIFO, device, or socket). The library throws this to prevent reading credentials from special files or being tricked into following a swapped path.
Solutions
- Point the credential path at a real regular file (check with `file` or `stat -c %F <path>`).
- If the path is a directory, append the credential filename (e.g. /path/to/dir/token.json instead of /path/to/dir).
- Recreate the credential with a plain file: remove the FIFO/device and write the secret with a normal file write.
- Ensure no volume manager or container mount is substituting a device/pipe at that path.
Example fix
// before
let creds = read_codewhale_owned_to_string(Path::new("/home/me/.codewhale/credentials"))?; // path is a directory
// after
let creds = read_codewhale_owned_to_string(Path::new("/home/me/.codewhale/credentials/token.json"))?; // regular file Defensive patterns
Strategy: validation
Validate before calling
fn ensure_regular_file(path: &Path) -> std::io::Result<()> {
let md = std::fs::metadata(path)?;
if !md.file_type().is_file() {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("{} is not a regular file", path.display())));
}
Ok(())
} Type guard
fn is_regular_file(path: &Path) -> bool {
std::fs::metadata(path).map(|m| m.file_type().is_file()).unwrap_or(false)
} Try / catch
match read_codewhale_owned_to_string(&path) {
Ok(creds) => use(creds),
Err(e) if e.kind() == std::io::ErrorKind::InvalidInput && e.to_string().contains("regular file") => {
eprintln!("credential path {} is not a regular file; fix the config", path.display());
}
Err(e) => return Err(e),
} Prevention
- Stat the path (file type, not just existence) before configuring it as a credential location.
- Never point credential config at directories, FIFOs, or /dev nodes.
- Use canonicalize() to resolve and inspect what a path actually is.
- Document the expected file type wherever credential paths are configured.
When it happens
Trigger: read_to_string or read_codewhale_owned_to_string is given a path whose open handle's metadata reports a non-regular file type: a directory, named pipe, /dev node, or socket was passed as the credential path.
Common situations: A config file points at a directory instead of a file; a credentials path is a FIFO created by a wrapper script; a symlink to /dev/null or a procfs pseudo-file is configured; a provisioning tool created the wrong file type.
Understand the failure class
Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.
Related errors
- Codewhale-owned credential file must be singly linked
- Codewhale-owned credential file must be singly linked…
- credential handoff could not write to stdout
- external credential path contains a NUL byte
- external credential path must be absolute
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/3b47be8c5fa975c1.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/external_credentials.rs:224
// call and flags require no variadic mode.
let fd = unsafe { libc::openat(current.as_raw_fd(), component.as_ptr(), flags) };
if fd < 0 {
return Err(io::Error::last_os_error());
}
// SAFETY: `fd` is newly owned after the successful `openat`.
current = unsafe { File::from_raw_fd(fd) };
opened_leaf = leaf;
}
if !opened_leaf {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"external credential path must name a file",
));
}
let metadata = current.metadata()?;
if !metadata.file_type().is_file() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"external credential path must name a regular file",
));
}
if require_owner_only {
use std::os::unix::fs::MetadataExt as _;
// SAFETY: geteuid(2) dereferences no pointers.
if metadata.uid() != unsafe { libc::geteuid() }
|| metadata.mode() & 0o077 != 0
|| metadata.nlink() != 1
{
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Codewhale-owned credential file must be singly linked, owned by this user, and mode 0600 or stricter",
));
}
}
Ok(current)View on GitHub (pinned to 73e0f67d83)