{"record":{"id":"4fee9c4b69118561","repo":"denoland/deno","slug":"path-not-found-outside-root","errorCode":null,"errorMessage":"path not found (outside root): {}","messagePattern":"path not found \\(outside root\\): (.+?)","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"warning","filePath":"cli/rt/file_system.rs","lineNumber":1261,"sourceCode":"    &'a self,\n    path: &Path,\n    seen: &mut HashSet<PathBuf>,\n    case_sensitivity: FileSystemCaseSensitivity,\n  ) -> std::io::Result<(PathBuf, VfsEntryRef<'a>)> {\n    let relative_path = match path.strip_prefix(&self.root_path) {\n      Ok(p) => p,\n      Err(_) => {\n        // \"outside root\" is the common host-FS fallback path in\n        // desktop --hmr / runtime-dynamic imports. Don't print at all\n        // by default; the caller falls through to a real filesystem\n        // read. Set DENO_LOG=denort=debug to see it.\n        log::debug!(\n          target: \"denort\",\n          \"[VFS] path outside root '{}': {}\",\n          self.root_path.display(),\n          path.display(),\n        );\n        return Err(std::io::Error::new(\n          std::io::ErrorKind::NotFound,\n          format!(\"path not found (outside root): {}\", path.display()),\n        ));\n      }\n    };\n    let mut final_path = self.root_path.clone();\n    let mut current_entry = VfsEntryRef::Dir(&self.dir);\n    for component in relative_path.components() {\n      let component = component.as_os_str();\n      let current_dir = match current_entry {\n        VfsEntryRef::Dir(dir) => {\n          final_path.push(component);\n          dir\n        }\n        VfsEntryRef::Symlink(symlink) => {\n          let dest = symlink.resolve_dest_from_root(&self.root_path);\n          let (resolved_path, entry) =\n            self.find_entry_inner(&dest, seen, case_sensitivity)?;","sourceCodeStart":1243,"sourceCodeEnd":1279,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/cli/rt/file_system.rs#L1243-L1279","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst rel = someUserInput;                       // may contain ../../\nconst p = vfsRoot + '/' + rel;                   // -> path not found (outside root)\n\n// after\nimport { normalize } from \"jsr:@std/path/normalize\";\nconst p = vfsRoot + '/' + normalize('/' + rel);  // clamped inside root","handlingStrategy":"fallback","validationCode":"// Clamp VFS lookups inside the root before resolving\nimport { normalize, join } from \"jsr:@std/path\";\nfunction vfsSafe(root: string, rel: string): string {\n  const p = normalize(join(root, rel));\n  if (p !== root && !p.startsWith(root + \"/\")) {\n    throw new Error(`path escapes VFS root: ${rel}`);\n  }\n  return p;\n}","typeGuard":null,"tryCatchPattern":"// Mirror the runtime's own fallback: VFS first, real FS on NotFound\nlet data: Uint8Array;\ntry {\n  data = await Deno.readFile(vfsPath);\n} catch (e) {\n  if (e instanceof Deno.errors.NotFound) {\n    data = await Deno.readFile(realPath); // same path on the host filesystem\n  } else throw e;\n}","preventionTips":["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"],"tags":["compile","vfs","path-resolution","path-traversal","not-found"],"backgroundTag":"path-outside-allowed-root","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}