Hmbown/CodeWhale · error · io::Error
external credential path contains reparse point
Error message
external credential path contains reparse point {} What it means
This error means a component of the credential file path is (or is inside) a reparse point such as a symlink or junction. Codewhale walks each path component and rejects any with FILE_ATTRIBUTE_REPARSE_POINT to prevent symlink-based escapes from the intended file location.
Solutions
- Point the configuration at the real, non-symlink physical path of the credential file
- Replace the symlink with a real copy (or hardlink-free duplicate) of the credential file
- On Windows, ensure no junction/mount point exists in any parent directory of the path
Example fix
// before credentials_path = C:\Users\me\dotfiles\cred.json # symlink // after credentials_path = C:\Users\me\AppData\Roaming\Codewhale\cred.json
Defensive patterns
Strategy: validation
Validate before calling
fn path_has_no_reparse(path: &std::path::Path) -> bool {
let mut cur = path.to_path_buf();
while let Some(parent) = cur.parent() {
if let Ok(md) = std::fs::symlink_metadata(&cur) {
#[cfg(windows)]
if md.file_attributes() & 0x400 != 0 { return false; } // FILE_ATTRIBUTE_REPARSE_POINT
}
cur = parent.to_path_buf();
}
true
} Try / catch
match read_to_string(path) {
Ok(s) => use_credential(&s),
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
eprintln!("path contains a symlink/reparse point; use the physical path");
}
Err(e) => return Err(e.into()),
} Prevention
- Configure absolute, physical paths for credentials — never through symlinked dotfiles
- Test your configured path with symlink_metadata before relying on it
- Avoid junctioned or redirected profile directories for credential storage
When it happens
Trigger: Calling open_secure_regular_file (via read_to_string / read_codewhale_owned_to_string) with a path that contains a symbolic link, junction, or mount-point directory, or whose parent directories are symlinks.
Common situations: Credential path configured through a symlinked dotfiles directory (e.g. dotfiles managed with symlinks under Dropbox/chezmoi); a junctioned profile directory; a redirected config path pointing at a link.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- Codewhale-owned xAI OAuth path must not be a reparse point
- external credential path must name a non-reparse regular…
- is a reparse point, not a workspace-owned file
- owned skill root escapes anchor
- refusing non-regular or reparse-point config lock at
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/f4c5a8ea86dd2a1f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/external_credentials.rs:622
}
#[cfg(windows)]
fn reject_windows_reparse_components(path: &Path) -> io::Result<()> {
use std::os::windows::fs::MetadataExt;
use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT;
let mut current = std::path::PathBuf::new();
for component in path.components() {
current.push(component.as_os_str());
if matches!(
component,
std::path::Component::Prefix(_) | std::path::Component::RootDir
) {
continue;
}
let metadata = std::fs::symlink_metadata(¤t)?;
if metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
format!(
"external credential path contains reparse point {}",
codewhale_config::quote_os_path(¤t)
),
));
}
}
Ok(())
}
#[cfg(not(any(unix, windows)))]
fn open_secure_regular_file(_path: &Path, _require_owner_only: bool) -> io::Result<File> {
Err(io::Error::new(
io::ErrorKind::Unsupported,
"secure external credential reads are unsupported on this platform",
))
}View on GitHub (pinned to 73e0f67d83)