denoland/deno · error
Circular symlink detected: {} -> {}
Error message
Circular symlink detected: {} -> {} What it means
When deno compile builds the virtual filesystem for a standalone binary, it follows directory symlinks while tracking visited paths in a set. Revisiting a path means a symlink cycle; the VFS builder bails with the chain of visited targets plus the offending link, since packing a cycle would never terminate.
Source
Thrown at cli/lib/standalone/virtual_fs.rs:1187
VfsEntry::Symlink(VirtualSymlink {
name: name.to_string(),
dest_parts: VirtualSymlinkParts::from_path(target.as_ref()),
dest_is_dir,
})
},
|_| {
// ignore previously inserted
},
);
#[allow(clippy::disallowed_methods, reason = "ok, creating vfs")]
let target_metadata = std::fs::symlink_metadata(target.as_ref())
.with_context(|| {
format!("Reading symlink target '{}'", target.display())
})?;
if target_metadata.is_symlink() {
if !visited.insert(target.as_ref().to_path_buf()) {
// todo: probably don't error in this scenario
bail!(
"Circular symlink detected: {} -> {}",
visited
.iter()
.map(|p| p.display().to_string())
.collect::<Vec<_>>()
.join(" -> "),
target.display()
);
}
self.add_symlink_inner(&target, visited)
} else if target_metadata.is_dir() {
Ok(SymlinkTarget::Dir(target.0.into_owned()))
} else {
Ok(SymlinkTarget::File(target.0.into_owned()))
}
}
/// Adds the CJS export analysis to the provided file.View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Break the cycle: replace one side of the loop with a real directory or file
- Point symlinks at concrete targets outside the packed tree, or remove them
- Exclude the cyclical directory from the embedded set and ship those assets alongside the binary instead
Example fix
# before ln -s ../b a ln -s ../a b # compile fails: Circular symlink detected: a -> b -> a # after # make one side a real directory cp -RL a a.real && rm a && mv a.real a
Defensive patterns
Strategy: validation
Validate before calling
# Detect symlink cycles before deno compile
find . -type l -exec realpath -e {} \; 2>/dev/null | sort | uniq -d | while read -r t; do
echo "multiple links resolve to $t - check for a cycle"
done
# or list directory symlinks that point back into the tree
find . -type l | while read -r l; do
case "$(readlink "$l")" in
.*|/*"$PWD"*) echo "suspicious link: $l -> $(readlink "$l")";;
esac
done Prevention
- Avoid directory symlinks inside trees that deno compile embeds; prefer real directories or file copies
- Run a cycle check over symlinks in the build script before invoking deno compile
- Read the chain in the error message - it shows the exact loop that must be broken
When it happens
Trigger: deno compile over a tree where symlinked directories form a loop - a -> b and b -> a, or a link pointing at an ancestor directory that is itself being packed.
Common situations: Monorepos with circular convenience links; alias directories created by tooling; manual links (ln -s ..) inside directories that get embedded.
Related errors
- app name {app_name:?} resolves its runtime library to {runti
- ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY
- ERR_FS_INVALID_SYMLINK_TYPE
- ERR_INCOMPATIBLE_OPTION_PAIR
- Unhandled scenario where a duplicate entry was found: {:?}
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/2f27e63f30a46fb9.
Report an issue: GitHub.