evanw/esbuild · error
EvalSymlinks: too many links
Error message
EvalSymlinks: too many links
What it means
esbuild vendors Go's path/filepath.EvalSymlinks logic (internal/fs/filepath.go). While resolving a path it counts symlink hops and aborts after 255 (linksWalked > 255) to prevent infinite loops, mirroring the Go runtime's own guard. This protects esbuild from pathological filesystem trees when it canonicalizes source, output, watch, or plugin-supplied paths. The error propagates up as a failed path resolution during a build.
Source
Thrown at internal/fs/filepath.go:244
// Resolve symlink.
fi, err := os.Lstat(dest)
if err != nil {
return "", err
}
if fi.Mode()&os.ModeSymlink == 0 {
if !fi.Mode().IsDir() && end < len(path) {
return "", syscall.ENOTDIR
}
continue
}
// Found symlink.
linksWalked++
if linksWalked > 255 {
return "", errors.New("EvalSymlinks: too many links")
}
link, err := os.Readlink(dest)
if err != nil {
return "", err
}
if isWindowsDot && !fp.isAbs(link) {
// On Windows, if "." is a relative symlink,
// just return ".".
break
}
path = link + path[end:]
v := fp.volumeNameLen(link)
if v > 0 {
// Symlink to drive name is an absolute path.View on GitHub (pinned to 6ff1d8b0d8)
Solutions
- Find and remove the offending symlink cycle: run 'ls -la' on the path esbuild prints and trace the links, or use 'readlink -f' to see where it stalls.
- Point esbuild at the real (non-symlinked) directory — set entry points / outdir / absWorkingDir to the canonical location.
- Audit node_modules and build output dirs for accidental self-referential symlinks from generators.
- If on a network/overlay filesystem, verify it isn't synthesizing infinite link chains.
Example fix
# before: node_modules/foo symlinks back to project root, creating a loop esbuild src/index.js --outfile=dist/index.js # after: break the cycle and target the real path readlink -f node_modules/foo # inspect rm node_modules/foo # remove the bad link esbuild src/index.js --outfile=dist/index.js
Defensive patterns
Strategy: validation
Validate before calling
const fs = require('fs');
function hasSymlinkLoop(p, max = 64) {
const seen = new Set();
let cur = p, n = 0;
while (true) { try { cur = fs.realpathSync(cur); } catch { return false; } if (seen.has(cur)) return true; seen.add(cur); if (++n > max) return true; if (fs.lstatSync(cur).Mode & fs.constants.S_IFLNK === 0) break; }
return false;
} Try / catch
try { await esbuild.build({...}); } catch (e) { if (/too many links/.test(e.message)) { /* scan entry/outdir/watch paths for symlink cycles */ } throw e; } Prevention
- Resolve entry points and outdir to realpath before passing to esbuild.
- Audit node_modules and generated dirs for self-referential symlinks.
- Avoid creating symlinks in build output directories.
- Run 'readlink -f' on suspect paths to detect loops pre-build.
When it happens
Trigger: Any esbuild operation that canonicalizes a path (resolving an entry point, outdir, absWorkingDir, plugin WatchFiles/WatchDirs, or serve servedir) encounters a symlink chain longer than 255 hops. Most often a circular symlink (a -> b -> a) or a deeply nested symlink farm.
Common situations: A symlink loop created by a bad post-install script or monorepo hoisting (node_modules -> itself); a self-referential symlink in a generated directory; corrupted or intentionally cyclic filesystems; mounting artifacts where directories are symlinked in a ring.
Related errors
- Rel: can't make {targpath} relative to {basepath}
- Invalid serve path: %s
- Invalid fallback path: %s
- Cannot compute relative path from %q to %q
- The "write" option is unavailable in this environment
AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03).
Data as JSON: /data/errors/538c5f3073fd45da.json.
Report an issue: GitHub.