Bug Description
When @vercel/nft traces dependencies in an environment containing broken symlinks
(symlinks whose target does not exist), the tracing process throws an unhandled
ENOENT error and aborts entirely.
Steps to Reproduce
- Create a broken symlink in the system (e.g.,
/etc/alternatives/which.sl1.gz
pointing to a non-existent target)
- Run
nodeFileTrace() on an entry file whose dependency chain resolves through
that directory (e.g., via a binary that references /usr/bin/which →
/etc/alternatives/which)
- The trace fails with: Error: ENOENT: no such file or directory, stat '/etc/alternatives/which.sl1.gz'
Expected Behavior
Broken symlinks should be skipped gracefully (similar to how _internalReadlink
already handles ENOENT by returning null). The trace should continue and
simply not include the broken symlink target in the output fileList.
Actual Behavior
The entire trace aborts with an unhandled ENOENT error, preventing any output
from being generated.
Environment
@vercel/nft version: 0.29.2
- Node.js: 20.x
- OS: Linux (CI container based on CentOS/Alpine with stale symlinks in
/etc/alternatives)
Analysis
In src/fs.ts, _internalStat correctly returns null for ENOENT. However,
the realpath() method in src/node-file-trace.ts recursively resolves paths
by calling readlink → resolve → realpath again. During this recursion,
when the resolved path traverses a directory that contains broken symlinks,
subsequent operations on adjacent files (like glob-based asset emission or
shared library detection) may attempt to stat those broken symlinks through
a code path that does not handle the null/ENOENT return gracefully.
Suggested Fix
Ensure that any code path that calls stat() or processes directory entries
during tracing handles broken symlinks (stat returning null) by skipping
the entry rather than propagating the error.
Bug Description
When
@vercel/nfttraces dependencies in an environment containing broken symlinks(symlinks whose target does not exist), the tracing process throws an unhandled
ENOENTerror and aborts entirely.Steps to Reproduce
/etc/alternatives/which.sl1.gzpointing to a non-existent target)
nodeFileTrace()on an entry file whose dependency chain resolves throughthat directory (e.g., via a binary that references
/usr/bin/which→/etc/alternatives/which)Expected Behavior
Broken symlinks should be skipped gracefully (similar to how
_internalReadlinkalready handles
ENOENTby returningnull). The trace should continue andsimply not include the broken symlink target in the output
fileList.Actual Behavior
The entire trace aborts with an unhandled ENOENT error, preventing any output
from being generated.
Environment
@vercel/nftversion: 0.29.2/etc/alternatives)Analysis
In
src/fs.ts,_internalStatcorrectly returnsnullfor ENOENT. However,the
realpath()method insrc/node-file-trace.tsrecursively resolves pathsby calling
readlink→resolve→realpathagain. During this recursion,when the resolved path traverses a directory that contains broken symlinks,
subsequent operations on adjacent files (like glob-based asset emission or
shared library detection) may attempt to
statthose broken symlinks througha code path that does not handle the null/ENOENT return gracefully.
Suggested Fix
Ensure that any code path that calls
stat()or processes directory entriesduring tracing handles broken symlinks (stat returning
null) by skippingthe entry rather than propagating the error.