denoland/deno · error

workspace root is not a local directory

Error message

workspace root is not a local directory

What it means

`deno check` (native type-checker path) converts the workspace root URL to a local directory path because the whole native pipeline (generated tsconfig, incremental cache keyed by paths) assumes a real filesystem root. If the workspace root is not a `file://` URL, the conversion fails and checking aborts.

Solutions

  1. Run `deno check` from a real local directory that contains (or is covered by) a local deno.json/deno.jsonc
  2. Verify the project root is an actual on-disk directory and not a virtual/remote mount
  3. If the setup looks valid and it still fails, attach `deno info` output and report an issue
Defensive patterns

Strategy: validation

Validate before calling

// Preflight for scripts that drive deno check
const cwd = Deno.cwd();
const info = Deno.statSync(cwd);
if (!info.isDirectory) {
  throw new Error("deno check requires a local directory workspace root");
}

Prevention

When it happens

Trigger: A workspace whose root resolves to a non-file URL — configuration discovered from a remote/virtual context, or embedded/sandboxed hosts where the project root is not a local directory on disk.

Common situations: Rare: running deno check inside unusual sandboxes, containers with virtual mounts, or environments where cwd/config resolution yields a non-file root. Everyday local projects never hit it.

Related errors


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

Appendix: source

Thrown at cli/tools/check.rs:73

  check_flags: CheckFlags,
) -> Result<(), AnyError> {
  if check_flags.doc || check_flags.doc_only {
    // Doc snippet extraction was handled by Deno 2.x's forked tsc; the native
    // compiler does not type-check markdown/JSDoc snippets yet.
    log::warn!(
      "{} --doc/--doc-only is not yet supported by the native type checker and will be ignored",
      colors::yellow("Warning")
    );
  }

  let factory = CliFactory::from_flags(flags.clone());
  let cli_options = factory.cli_options()?;
  let project_root = cli_options
    .workspace()
    .root_dir_url()
    .to_file_path()
    .map_err(|_| {
      deno_core::anyhow::anyhow!("workspace root is not a local directory")
    })?;

  // Build the module graph over the requested roots (or the whole project).
  // Deno owns resolution: this drives deno's own graph diagnostics (missing
  // modules + hints) and the incremental type-check cache, so we can skip the
  // external compiler entirely when nothing it sees has changed.
  // Resolve the requested roots the same way `deno check` always has:
  // globs are expanded, workspace `exclude` is applied, and
  // `include_ignored_specified: false` means an explicitly-passed excluded file
  // is skipped rather than force-checked. An empty result is not an error - it
  // just means there's nothing to check (e.g. every match was excluded), which
  // deno reports as a warning and a clean exit.
  let graph_container = factory.main_module_graph_container().await?;
  let mut roots = graph_container.collect_specifiers(
    &check_flags.files,
    crate::graph_container::CollectSpecifiersOptions {
      include_ignored_specified: false,
    },

View on GitHub (pinned to 9ad36f7a2c)