denoland/deno · error

On Windows the target must be a file or directory

Error message

On Windows the target must be a file or directory

What it means

On Windows, Deno.symlink()/symlinkSync() must know whether to create a file or directory symlink because the OS APIs differ. When options omits the type, Deno stats oldpath to infer it; if the metadata exists but is neither a regular file nor a directory (named pipe, device, etc.), the call fails with io::ErrorKind::InvalidInput 'On Windows the target must be a file or directory' (ext/fs/std_fs.rs:1233).

Source

Thrown at ext/fs/std_fs.rs:1233

#[cfg(windows)]
fn symlink(
  oldpath: &Path,
  newpath: &Path,
  file_type: Option<FsFileType>,
) -> FsResult<()> {
  let file_type = match file_type {
    Some(file_type) => file_type,
    None => {
      let old_meta = fs::metadata(oldpath);
      match old_meta {
        Ok(metadata) => {
          if metadata.is_file() {
            FsFileType::File
          } else if metadata.is_dir() {
            FsFileType::Directory
          } else {
            return Err(FsError::Io(io::Error::new(
              io::ErrorKind::InvalidInput,
              "On Windows the target must be a file or directory",
            )));
          }
        }
        Err(err) if err.kind() == io::ErrorKind::NotFound => {
          return Err(FsError::Io(io::Error::new(
            io::ErrorKind::InvalidInput,
            "On Windows an `options` argument is required if the target does not exist",
          )));
        }
        Err(err) => return Err(err.into()),
      }
    }
  };

  match file_type {
    FsFileType::File => {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Pass the type explicitly: Deno.symlinkSync(target, link, { type: 'file' }) (or 'dir')
  2. Rely on inference only when the target is a plain file or directory
  3. Branch on Deno.build.os and skip symlinking special files on Windows

Example fix

// before
Deno.symlinkSync(target, link); // target is a named pipe on Windows

// after
Deno.symlinkSync(target, link, { type: 'file' });
Defensive patterns

Strategy: validation

Validate before calling

function symlinkSyncSafe(target: string, link: string): void {
  let type: 'file' | 'dir' | undefined;
  if (Deno.build.os === 'windows') {
    const st = Deno.statSync(target); // follows link; throws if missing
    type = st.isDirectory ? 'dir' : 'file';
    if (!st.isFile && !st.isDirectory) {
      throw new Error(`Cannot infer Windows link type for special file: ${target}`);
    }
  }
  Deno.symlinkSync(target, link, type ? { type } : undefined);
}

Type guard

function isPlainFsEntry(path: string): boolean {
  try {
    const st = Deno.statSync(path);
    return st.isFile || st.isDirectory;
  } catch {
    return false;
  }
}

Prevention

When it happens

Trigger: Deno.symlinkSync(target, link) on Windows where target exists but is a named pipe or other special file; scripts ported from Unix that freely symlink special files.

Common situations: Windows dev boxes creating tool shims whose target is a pipe; provisioning scripts that mirror Unix /dev-style layouts.

Related errors


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