denoland/deno · error · std::io::Error
path not found (symlink not dir): {}
Error message
path not found (symlink not dir): {} What it means
During VFS path traversal, when a path component lands on a symlink the resolver follows it (recursively, with cycle detection) and expects the resolved entry to be a directory so traversal can continue. If the symlink resolves to a file (or the recursion returns a non-directory), resolution fails with io::ErrorKind::NotFound 'path not found (symlink not dir): <p>' — the requested path implies directory components under a symlink that points at a file.
Source
Thrown at cli/rt/file_system.rs:1287
for component in relative_path.components() {
let component = component.as_os_str();
let current_dir = match current_entry {
VfsEntryRef::Dir(dir) => {
final_path.push(component);
dir
}
VfsEntryRef::Symlink(symlink) => {
let dest = symlink.resolve_dest_from_root(&self.root_path);
let (resolved_path, entry) =
self.find_entry_inner(&dest, seen, case_sensitivity)?;
final_path = resolved_path; // overwrite with the new resolved path
match entry {
VfsEntryRef::Dir(dir) => {
final_path.push(component);
dir
}
_ => {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("path not found (symlink not dir): {}", path.display()),
));
}
}
}
_ => {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("path not found (not dir): {}", path.display()),
));
}
};
let component = component.to_string_lossy();
current_entry = current_dir
.entries
.get_by_name(&component, case_sensitivity)
.ok_or_else(|| {View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Check what the symlink actually points at: Deno.readLinkSync / Deno.statSync (which follows links) on the path without the extra child segments
- Build paths from the resolved target: resolve the symlink first, then append remaining components to the resolved directory
- Validate directory-ness of each ancestor (statSync(...).isDirectory) before appending children in generic path-walking code
- If the VFS content is yours (custom compile inputs), avoid symlinks-to-files where consumers expect directories
Example fix
// before const p = linkPath + '/index.js'; // linkPath is a symlink to a file Deno.readTextFileSync(p); // path not found (symlink not dir) // after const real = Deno.statSync(linkPath); // follows the symlink const base = real.isDirectory ? linkPath : stdDirOf(linkPath); Deno.readTextFileSync(base + '/index.js');
Defensive patterns
Strategy: validation
Validate before calling
// Verify each ancestor is a directory (following symlinks) before descent
function assertResolvableDirChain(p: string) {
const parts = p.split("/").filter(Boolean);
let cur = "";
for (const part of parts) {
cur += `/${part}`;
const st = Deno.statSync(cur); // follows symlinks
if (!st.isDirectory && cur !== p) throw new Error(`non-directory in path: ${cur}`);
}
} Type guard
function isDirOrSymlinkToDir(p: string): boolean {
try { return Deno.statSync(p).isDirectory; } catch { return false; }
} Try / catch
try {
content = await Deno.readTextFile(`${linkPath}/${child}`);
} catch (e) {
if (e instanceof Deno.errors.NotFound && isDirOrSymlinkToDir(linkPath) === false) {
// linkPath is a symlink to a file: resolve it and read the file directly
content = await Deno.readTextFile(Deno.realPathSync(linkPath));
} else throw e;
} Prevention
- Resolve symlinks (realPathSync/statSync) before appending child segments
- When walking npm-style trees, readLink first and branch on target type
- Prefer building paths from resolved targets instead of link intermediates
When it happens
Trigger: A compiled app addressing something like <dir-symlink-to-file>/child.js where the VFS contains a symlink (e.g. npm package bin or .bin style links) pointing to a file, but the code treats the link itself as a directory; path joining that appends segments onto a symlinked file path.
Common situations: Node-compat tooling walking node_modules/.bin or package 'main' symlinks and assuming directories; building paths from import.meta.url chains that pass through symlinked entries inside npm packages in the VFS; case-mismatch making an intended dir lookup land on a file link.
Related errors
- path not found (outside root): {}
- Not a directory
- {} is not supported for an embedded deno compile file
- path not found (not dir): {}
- On Windows an `options` argument is required if the target d
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/5549121fad9e118b.
Report an issue: GitHub.