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

  1. 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.
  2. Probe the path with `Deno.stat` at startup and log the offending component before the failing call.
  3. 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

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


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