{"record":{"id":"29a56460e8f96f93","repo":"denisidoro/navi","slug":"has-no-parent","errorCode":null,"errorMessage":"`{}` has no parent","messagePattern":"`(.+?)` has no parent","errorType":"exception","errorClass":"anyhow","httpStatus":null,"severity":"error","filePath":"src/common/fs.rs","lineNumber":69,"sourceCode":"    Ok(pathbuf\n        .as_ref()\n        .as_os_str()\n        .to_str()\n        .ok_or_else(|| InvalidPath(pathbuf.as_ref().to_path_buf()))\n        .map(str::to_string)?)\n}\n\nfn follow_symlink(pathbuf: PathBuf) -> Result<PathBuf> {\n    read_link(pathbuf.clone())\n        .map(|o| {\n            let o_str = o\n                .as_os_str()\n                .to_str()\n                .ok_or_else(|| InvalidPath(o.to_path_buf()))?;\n            if o_str.starts_with('.') {\n                let p = pathbuf\n                    .parent()\n                    .ok_or_else(|| anyhow!(\"`{}` has no parent\", pathbuf.display()))?;\n                let mut p = PathBuf::from(p);\n                p.push(o_str);\n                follow_symlink(p)\n            } else {\n                follow_symlink(o)\n            }\n        })\n        .unwrap_or(Ok(pathbuf))\n}\n\nfn exe_pathbuf() -> Result<PathBuf> {\n    let pathbuf = current_exe().context(\"Unable to acquire executable's path\")?;\n\n    #[cfg(target_family = \"windows\")]\n    let pathbuf = dunce::canonicalize(pathbuf)?;\n\n    debug!(current_exe = ?pathbuf);\n    follow_symlink(pathbuf)","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/denisidoro/navi/blob/f7330b9ad5bd95b7d1a3c96d00e0a77deb589147/src/common/fs.rs#L51-L87","documentation":"`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())`.","triggerScenarios":"`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 '.'.","commonSituations":"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.","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"],"exampleFix":"// before\nlet p = PathBuf::from(\"navi\");\nfs::follow_symlink(p)?; // panic: `navi` has no parent\n// after\nlet p = std::env::current_dir()?.join(\"navi\");\nfs::follow_symlink(p)?;","handlingStrategy":"validation","validationCode":"fn has_parent(p: &std::path::Path) -> bool {\n    p.parent().is_some()\n}\nlet path = std::path::PathBuf::from(\"navi\");\nif !has_parent(&path) {\n    let path = std::env::current_dir()?.join(&path); // anchor to cwd first\n}\nfs::follow_symlink(&path)?;","typeGuard":"fn is_resolvable_symlink_path(p: &std::path::Path) -> bool {\n    p.is_absolute() || p.parent().is_some()\n}","tryCatchPattern":"match fs::follow_symlink(&path) {\n    Ok(resolved) => use_resolved(resolved),\n    Err(e) if e.to_string().contains(\"has no parent\") => {\n        let anchored = std::env::current_dir()?.join(&path);\n        use_resolved(fs::follow_symlink(&anchored)?);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["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"],"tags":["filesystem","symlink","path-resolution","panic"],"backgroundTag":"symlink-resolution-failed","analyzedSha":"f7330b9ad5bd95b7d1a3c96d00e0a77deb589147","analyzedAt":"2026-09-03T13:58:22.429Z","contentChangedAt":"2026-09-03T13:58:22.429Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}