{"id":"538c5f3073fd45da","repo":"evanw/esbuild","slug":"evalsymlinks-too-many-links","errorCode":null,"errorMessage":"EvalSymlinks: too many links","messagePattern":"EvalSymlinks: too many links","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/fs/filepath.go","lineNumber":244,"sourceCode":"\t\t// Resolve symlink.\n\n\t\tfi, err := os.Lstat(dest)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tif fi.Mode()&os.ModeSymlink == 0 {\n\t\t\tif !fi.Mode().IsDir() && end < len(path) {\n\t\t\t\treturn \"\", syscall.ENOTDIR\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// Found symlink.\n\n\t\tlinksWalked++\n\t\tif linksWalked > 255 {\n\t\t\treturn \"\", errors.New(\"EvalSymlinks: too many links\")\n\t\t}\n\n\t\tlink, err := os.Readlink(dest)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tif isWindowsDot && !fp.isAbs(link) {\n\t\t\t// On Windows, if \".\" is a relative symlink,\n\t\t\t// just return \".\".\n\t\t\tbreak\n\t\t}\n\n\t\tpath = link + path[end:]\n\n\t\tv := fp.volumeNameLen(link)\n\t\tif v > 0 {\n\t\t\t// Symlink to drive name is an absolute path.","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/evanw/esbuild/blob/6ff1d8b0d8c134e867a397eef39702a223ebef9e/internal/fs/filepath.go#L226-L262","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before: node_modules/foo symlinks back to project root, creating a loop\nesbuild src/index.js --outfile=dist/index.js\n\n# after: break the cycle and target the real path\nreadlink -f node_modules/foo   # inspect\nrm node_modules/foo            # remove the bad link\nesbuild src/index.js --outfile=dist/index.js","handlingStrategy":"validation","validationCode":"const fs = require('fs');\nfunction hasSymlinkLoop(p, max = 64) {\n  const seen = new Set();\n  let cur = p, n = 0;\n  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; }\n  return false;\n}","typeGuard":null,"tryCatchPattern":"try { await esbuild.build({...}); } catch (e) { if (/too many links/.test(e.message)) { /* scan entry/outdir/watch paths for symlink cycles */ } throw e; }","preventionTips":["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."],"tags":["esbuild","filesystem","symlink","path-resolution"],"analyzedSha":"6ff1d8b0d8c134e867a397eef39702a223ebef9e","analyzedAt":"2026-08-03T19:42:38.433Z","schemaVersion":2}