denoland/deno · error
path not found (not dir): {}
Error message
path not found (not dir): {} What it means
Thrown while resolving a path inside the embedded virtual filesystem (VFS) of a compiled Deno binary. `find_entry_no_follow` walks the path component by component; when an intermediate component resolves to a VFS entry that is neither a directory nor a followable symlink (i.e. a regular file), traversal cannot descend into it and the lookup fails with `io::ErrorKind::NotFound`. The `{}` placeholder is the full requested path.
Source
Thrown at cli/rt/file_system.rs:1295
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(|| {
std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("path not found (entry missing): {}", path.display()),
)
})?
.as_ref();
}
View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Check the printed path and remove the segment that follows the file component — the file named in the middle of the path is being treated as a directory.
- Probe the path with `Deno.stat` at startup and log the offending component before the failing call.
- If the layout is intentional, restructure the assets so containers are directories, then recompile with `deno compile`.
Example fix
// before
const cfg = await Deno.readTextFile("./assets/data.json/part"); // data.json is a file
// after
const cfg = await Deno.readTextFile("./assets/data.json"); Defensive patterns
Strategy: validation
Validate before calling
// probe the parent chain before first use (compiled binaries)
async function assertTraversable(path: string) {
const parts = path.split(/[\\/]/).filter(Boolean);
let cur = ".";
for (const p of parts.slice(0, -1)) {
cur += "/" + p;
const st = await Deno.stat(cur); // throws NotFound if missing
if (!st.isDirectory) throw new Error(`not a directory: ${cur}`);
}
} Type guard
function isDirectory(st: Deno.FileInfo | null): st is Deno.FileInfo {
return st !== null && st.isDirectory;
} Try / catch
try {
const data = await Deno.readFile(p);
} catch (err) {
if (err instanceof Deno.errors.NotFound) {
// inspect err.message: "(not dir)" = mid-path file, "(entry missing)" = absent leaf
} else throw err;
} Prevention
- Build paths from typed segments, never string concatenation of file paths and suffixes
- Smoke-test compiled binaries against the real asset tree in CI
- Validate asset layouts with a startup probe before the hot code path
When it happens
Trigger: Any file-system API call against the embedded VFS where a non-final path component names a file, e.g. `Deno.readTextFile("./mod.ts/helper")` in a `deno compile` binary, or code that concatenates a filename with an extra `/segment`. It fires during component descent, before the final name is even looked up.
Common situations: Typos appending a suffix after a filename ("./data.bin" + "/part"); path templates where a file sits where the code expects a directory; assets restructured after the code was written but before the binary was rebuilt.
Related errors
- path not found (entry missing): {}
- failed to resolve startup order {}: {error}
- Unhandled scenario where a duplicate entry was found: {:?}
- Import map not found in eszip: {}
- path not found (outside root): {}
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/65d19d00846e02c7.
Report an issue: GitHub.