denisidoro/navi · error · anyhow
`{}` has no parent
Error message
`{}` has no parent What it means
`common::fs::follow_symlink` resolves symlink components. When a symlink's stored target is a relative path starting with '.', the code needs the symlink's own parent directory to re-anchor the relative path; `PathBuf::parent()` returning None (no parent component, e.g. a bare relative filename) triggers `anyhow!("`{}` has no parent", pathbuf.display())`.
Source
Thrown at src/common/fs.rs:69
Ok(pathbuf
.as_ref()
.as_os_str()
.to_str()
.ok_or_else(|| InvalidPath(pathbuf.as_ref().to_path_buf()))
.map(str::to_string)?)
}
fn follow_symlink(pathbuf: PathBuf) -> Result<PathBuf> {
read_link(pathbuf.clone())
.map(|o| {
let o_str = o
.as_os_str()
.to_str()
.ok_or_else(|| InvalidPath(o.to_path_buf()))?;
if o_str.starts_with('.') {
let p = pathbuf
.parent()
.ok_or_else(|| anyhow!("`{}` has no parent", pathbuf.display()))?;
let mut p = PathBuf::from(p);
p.push(o_str);
follow_symlink(p)
} else {
follow_symlink(o)
}
})
.unwrap_or(Ok(pathbuf))
}
fn exe_pathbuf() -> Result<PathBuf> {
let pathbuf = current_exe().context("Unable to acquire executable's path")?;
#[cfg(target_family = "windows")]
let pathbuf = dunce::canonicalize(pathbuf)?;
debug!(current_exe = ?pathbuf);
follow_symlink(pathbuf)View on GitHub (pinned to f7330b9ad5)
Solutions
- Pass an absolute path (or one with a parent component like ./navi) to follow_symlink/exe_pathbuf
- Canonicalize with std::fs::canonicalize before calling when possible
- Recreate the symlink with an absolute or properly relative (../-anchored) target
- Upstream: fall back to PathBuf::from(".") as parent instead of erroring
Example fix
// before
let p = PathBuf::from("navi");
fs::follow_symlink(p)?; // panic: `navi` has no parent
// after
let p = std::env::current_dir()?.join("navi");
fs::follow_symlink(p)?; Defensive patterns
Strategy: validation
Validate before calling
fn has_parent(p: &std::path::Path) -> bool {
p.parent().is_some()
}
let path = std::path::PathBuf::from("navi");
if !has_parent(&path) {
let path = std::env::current_dir()?.join(&path); // anchor to cwd first
}
fs::follow_symlink(&path)?; Type guard
fn is_resolvable_symlink_path(p: &std::path::Path) -> bool {
p.is_absolute() || p.parent().is_some()
} Try / catch
match fs::follow_symlink(&path) {
Ok(resolved) => use_resolved(resolved),
Err(e) if e.to_string().contains("has no parent") => {
let anchored = std::env::current_dir()?.join(&path);
use_resolved(fs::follow_symlink(&anchored)?);
}
Err(e) => return Err(e),
} Prevention
- Always pass absolute paths (current_dir().join(...)) to follow_symlink/exe_pathbuf
- Canonicalize inputs with std::fs::canonicalize when possible
- Create symlinks with absolute or properly anchored relative targets
- Treat bare single-component filenames as suspect inputs
When it happens
Trigger: `follow_symlink(path)` (recursively, or via `exe_pathbuf`) where `path` is a relative path with no parent directory component (e.g. "navi") and the file is a symlink whose target begins with '.'.
Common situations: Inspecting executables/symlinks installed with bare relative names in the current directory, broken or unusually-constructed relative symlinks, or programmatically passing a single-component path to exe_pathbuf.
Related errors
- Unable to parse {path}
- Invalid link
- {name} not set
- No files found
- Could not open file in external editor
AI-assisted analysis of denisidoro/navi@f7330b9ad5 (2026-09-03).
Data as JSON: /api/errors/29a56460e8f96f93.
Report an issue: GitHub.