denoland/deno · warning · std::io::Error
path not found (outside root): {}
Error message
path not found (outside root): {} What it means
When resolving a path inside a compiled binary's VFS, components that escape the VFS root (e.g. '..' sequences past the root) make the relative-path computation fail; the resolver returns io::ErrorKind::NotFound 'path not found (outside root): <p>'. The runtime itself treats this as a signal to fall back to the real host filesystem (used by desktop --hmr and runtime-dynamic imports), and logs it only at trace/debug level.
Source
Thrown at cli/rt/file_system.rs:1261
&'a self,
path: &Path,
seen: &mut HashSet<PathBuf>,
case_sensitivity: FileSystemCaseSensitivity,
) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> {
let relative_path = match path.strip_prefix(&self.root_path) {
Ok(p) => p,
Err(_) => {
// "outside root" is the common host-FS fallback path in
// desktop --hmr / runtime-dynamic imports. Don't print at all
// by default; the caller falls through to a real filesystem
// read. Set DENO_LOG=denort=debug to see it.
log::debug!(
target: "denort",
"[VFS] path outside root '{}': {}",
self.root_path.display(),
path.display(),
);
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("path not found (outside root): {}", path.display()),
));
}
};
let mut final_path = self.root_path.clone();
let mut current_entry = VfsEntryRef::Dir(&self.dir);
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)?;View on GitHub (pinned to 9ad36f7a2c)
Solutions
- If you call the VFS API directly, normalize paths first (std/path normalize/join from the VFS root) so '..' never climbs past the root
- Treat NotFound from the VFS as expected: catch it and retry against the real filesystem, mirroring the runtime's own fallback
- For code that must reach host files from a compiled app, use plain Deno.* FS APIs with real paths instead of VFS-rooted ones
- Set DENO_LOG=denort=debug if you need visibility into these fallback probes
Example fix
// before
const rel = someUserInput; // may contain ../../
const p = vfsRoot + '/' + rel; // -> path not found (outside root)
// after
import { normalize } from "jsr:@std/path/normalize";
const p = vfsRoot + '/' + normalize('/' + rel); // clamped inside root Defensive patterns
Strategy: fallback
Validate before calling
// Clamp VFS lookups inside the root before resolving
import { normalize, join } from "jsr:@std/path";
function vfsSafe(root: string, rel: string): string {
const p = normalize(join(root, rel));
if (p !== root && !p.startsWith(root + "/")) {
throw new Error(`path escapes VFS root: ${rel}`);
}
return p;
} Try / catch
// Mirror the runtime's own fallback: VFS first, real FS on NotFound
let data: Uint8Array;
try {
data = await Deno.readFile(vfsPath);
} catch (e) {
if (e instanceof Deno.errors.NotFound) {
data = await Deno.readFile(realPath); // same path on the host filesystem
} else throw e;
} Prevention
- Normalize user-supplied paths against the VFS root before lookup
- Treat NotFound from a virtual FS as an expected miss, not a fatal error
- Enable DENO_LOG=denort=debug when diagnosing VFS fallback behavior
When it happens
Trigger: A VFS lookup with a path like /deno-dir/../../etc/passwd where normalization leaves the root; code constructing paths from user input that traverse upward; fallback flows where the runtime probes the VFS first, gets this error, then reads the same path from the host FS — externally you only see it if you call the VFS layer directly or enable DENO_LOG=denort=debug.
Common situations: Dynamic import of a module outside the compile snapshot (by design falls through to real FS); HMR flows in desktop-ish setups; security-sensitive code auditing path handling; direct users of the FileBackedVfs API passing absolute host paths that don't root-ify.
Related errors
- path not found (symlink not dir): {}
- Not a directory
- {} is not supported for an embedded deno compile file
- path not found (not dir): {}
- Unexpected second argument to Deno.bench()
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/4fee9c4b69118561.
Report an issue: GitHub.