denoland/deno · error

{}, symlink '{}' -> '{}'

Error message

{}, symlink '{}' -> '{}'

What it means

symlink_dir() creates a directory symlink for npm package linking and rewraps any OS-level failure with this message, appending the oldpath -> newpath pair so the failing link is identifiable. The underlying error kind is preserved (or specially mapped on Windows), so the real cause — permissions, existing target, unsupported filesystem — is the inner part of the message. It is a context wrapper, not a distinct failure mode of its own.

Source

Thrown at libs/npm_installer/fs.rs:117

      // esbuild's install script hardlinks its platform package's binary
      // over its own JS shim) and copying in place would write through
      // the link, corrupting the file at its other paths. Removing first
      // also breaks hardlinks to currently-executing binaries (ETXTBSY).
      let _ = sys.fs_remove_file(&new_to);
      sys.fs_copy(&new_from, &new_to)?;
    }
  }

  Ok(())
}

pub fn symlink_dir<TSys: sys_traits::BaseFsSymlinkDir>(
  sys: &TSys,
  oldpath: &Path,
  newpath: &Path,
) -> Result<(), Error> {
  let err_mapper = |err: Error, kind: Option<ErrorKind>| {
    Error::new(
      kind.unwrap_or_else(|| err.kind()),
      format!(
        "{}, symlink '{}' -> '{}'",
        err,
        oldpath.display(),
        newpath.display()
      ),
    )
  };

  sys.fs_symlink_dir(oldpath, newpath).map_err(|err| {
    #[cfg(windows)]
    if let Some(code) = err.raw_os_error()
      && (code as u32
        == windows_sys::Win32::Foundation::ERROR_PRIVILEGE_NOT_HELD
        || code as u32
          == windows_sys::Win32::Foundation::ERROR_INVALID_FUNCTION)
    {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Read the inner OS error in the message: on Windows 'Permission denied' means enabling Developer Mode (Settings > Privacy & Security > For developers) or running the terminal elevated.
  2. Delete the existing node_modules directory (and the specific newpath from the message if it exists) and re-run the install.
  3. Move node_modules to a filesystem that supports symlinks, or switch the npm install to a linker mode that copies instead of symlinking (e.g. --vendor / local node_modules with hoisted mode).

Example fix

# before (Windows, symlink privilege missing)
deno install npm:chalk   # ..., symlink '.../chalk' -> '.../node_modules/.deno/chalk@5.0.0/lib'

# after
# 1) enable Developer Mode (or run PowerShell as Administrator)
# 2) clean stale links and retry
rm -rf node_modules && deno install npm:chalk
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight on Windows: can we create symlinks? (cheap probe)
#[cfg(windows)]
fn symlink_privilege_ok(temp: &std::path::Path) -> bool {
  let probe = temp.join("deno_symlink_probe");
  let _ = std::fs::remove_file(&probe);
  std::os::windows::fs::symlink_file(temp, &probe).is_ok()
    && { let _ = std::fs::remove_file(&probe); true }
}

Try / catch

if let Err(err) = symlink_dir(sys, &oldpath, &newpath) {
  let msg = err.to_string();
  if msg.contains("symlink '") {
    match err.kind() {
      std::io::ErrorKind::PermissionDenied => {
        // Windows: enable Developer Mode or run elevated; else fall back to copying
        fallback_copy_dir(sys, &oldpath, &newpath)?;
      }
      std::io::ErrorKind::AlreadyExists => {
        sys.fs_remove_dir_all(&newpath)?;
        symlink_dir(sys, &oldpath, &newpath)?;
      }
      _ => return Err(err),
    }
  } else {
    return Err(err);
  }
}

Prevention

When it happens

Trigger: Populating node_modules with symlinked dependencies (isolated/by-symlink npm linking) when the OS symlink call fails: Windows without SeCreateSymbolicLinkPrivilege, target path already existing, or a filesystem/volume that forbids symlinks.

Common situations: Windows without Developer Mode and running unelevated; CI containers with restricted privileges; node_modules on network filesystems (NFS/SMB) or FAT/exFAT volumes; stale node_modules from a previous linker mode; Docker volume mounts that disallow symlinks.

Related errors


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