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

  1. If you call the VFS API directly, normalize paths first (std/path normalize/join from the VFS root) so '..' never climbs past the root
  2. Treat NotFound from the VFS as expected: catch it and retry against the real filesystem, mirroring the runtime's own fallback
  3. For code that must reach host files from a compiled app, use plain Deno.* FS APIs with real paths instead of VFS-rooted ones
  4. 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

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


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/4fee9c4b69118561. Report an issue: GitHub.