denoland/deno · error
refusing to materialize package into symlinked directory
Error message
refusing to materialize package into symlinked directory
What it means
ensure_not_symlink() is called before an npm package is extracted/materialized into its cache directory. If fs_symlink_metadata shows the target path is a symlink, it refuses with ErrorKind::AlreadyExists and this message, because writing package files through a symlink would scatter them into an unintended location and could silently mutate whatever the link points at. A missing path (NotFound) is fine; only an existing symlink is rejected.
Source
Thrown at libs/npm_cache/fs_util.rs:74
where
TSys: FsCreateDirAll + FsMetadata,
{
ensure_not_symlink(sys, path)?;
sys.fs_create_dir_all(path)?;
ensure_not_symlink(sys, path)
}
/// Returns an error when the path is a symlink.
pub fn ensure_not_symlink<TSys>(
sys: &TSys,
path: &Path,
) -> Result<(), std::io::Error>
where
TSys: FsMetadata,
{
match sys.fs_symlink_metadata(path) {
Ok(metadata) if metadata.file_type().is_symlink() => {
Err(std::io::Error::new(
ErrorKind::AlreadyExists,
"refusing to materialize package into symlinked directory",
))
}
Ok(_) => Ok(()),
Err(err) if err.kind() == ErrorKind::NotFound => Ok(()),
Err(err) => Err(err),
}
}
#[sys_traits::auto_impl]
pub trait HardLinkFileSys: FsHardLink + FsRemoveFile + ThreadSleep {}
/// Hardlinks a file from one location to another.
pub fn hard_link_file<TSys: HardLinkFileSys>(
sys: &TSys,
from: &Path,
to: &Path,View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Run ls -la on the path from the error context and replace the symlink with a real directory (mkdir, then copy the contents back if needed).
- Point DENO_DIR (or the npm cache setting) at a location that contains no symlinks anywhere along the path.
- If the symlink came from a sync/backup tool, exclude the cache directory from that tool and restore a real directory.
Example fix
# before ln -s /mnt/big/deno-npm ~/.cache/deno/npm # every tarball extract now fails # after rm ~/.cache/deno/npm mkdir -p ~/.cache/deno/npm # real directory; extraction succeeds
Defensive patterns
Strategy: validation
Validate before calling
use std::fs;
fn cache_target_is_clean(path: &std::path::Path) -> std::io::Result<()> {
match fs::symlink_metadata(path) {
Ok(md) if md.file_type().is_symlink() => Err(std::io::Error::new(
std::io::ErrorKind::AlreadyExists,
format!("{} is a symlink; replace it with a real directory", path.display()),
)),
Ok(_) => Ok(()),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
Err(e) => Err(e),
}
} Try / catch
match ensure_cache_dir(&path) {
Ok(()) => { /* extract */ }
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists
&& err.to_string().contains("symlink") =>
{
// tell the user to replace the symlinked cache dir with a real directory
}
Err(err) => return Err(err),
} Prevention
- Never symlink DENO_DIR or its npm subdirectory; if space is the concern, move the whole DENO_DIR to the bigger disk and set the env var to the real path.
- Exclude the Deno cache from sync/backup tools (iCloud, Dropbox) that can replace directories with symlinks.
- In setup scripts, assert the cache path is a real directory (fs::symlink_metadata check) before installs run.
When it happens
Trigger: Extracting an npm tarball into $DENO_DIR/npm/... (or any configured npm cache dir) when the package's version directory, or a parent of it, is a symlink — e.g. someone symlinked the cache to another disk, or a backup/sync tool (iCloud, Dropbox) replaced directories with links.
Common situations: Symlinking DENO_DIR or the npm cache subdirectory to save disk space; shared caches between machines via network volumes; macOS file providers converting folders to symlink placeholders; deliberately symlinking node_modules in monorepos while the cache path collides with it.
Related errors
- On Windows the target must be a file or directory
- On Windows an `options` argument is required if the target d
- tar entry '{}' extends beyond archive (offset={}, size={}, a
- {}, symlink '{}' -> '{}'
- ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/6d689f0beef359e1.
Report an issue: GitHub.