Summary
Link.matches (@npmcli/arborist/lib/node.js:1183) dereferences this.target without a null check:
if (this.isLink) {
return node.isLink && this.target.matches(node.target)
}
When node_modules contains store-style symlinks whose targets npm hasn't loaded as nodes - e.g. a tree populated by bun (node_modules/.bun/...) or vlt (node_modules/.vlt/...), neither of which writes node_modules/.package-lock.json - this.target is null, so any npm install crashes mid-reify with an unhandled TypeError instead of a clean, actionable error.
Steps to reproduce
- In any npm workspace monorepo, install deps with another package manager:
vlt install (or bun install).
- Run
npm install.
Actual
TypeError: Cannot read properties of null (reading 'matches')
at Link.matches (@npmcli/arborist/lib/node.js:1183:41)
at Link.canDedupe (lib/node.js:1127:15)
at PlaceDep.pruneDedupable (lib/place-dep.js:426:14)
at new PlaceDep (lib/place-dep.js:278:14)
at #buildDepStep (lib/arborist/build-ideal-tree.js:933:18)
at async Arborist.buildIdealTree (lib/arborist/build-ideal-tree.js:170:7)
at async Arborist.reify (lib/arborist/reify.js:111:5)
at async Install.exec (lib/commands/install.js:146:5)
Expected
A clean error (e.g. "node_modules was not installed by npm"), not an unhandled TypeError. Minimal fix: guard the link branch, e.g. return node.isLink && !!this.target && this.target.matches(node.target).
Alternatively, regenerate the hidden lockfile whenever it's absent.
Environment
npm 11.13.0, Node 26 (also reproduced on Node 22/24 in CI).
Summary
Link.matches(@npmcli/arborist/lib/node.js:1183) dereferencesthis.targetwithout a null check:When
node_modulescontains store-style symlinks whose targets npm hasn't loaded as nodes - e.g. a tree populated by bun (node_modules/.bun/...) or vlt (node_modules/.vlt/...), neither of which writesnode_modules/.package-lock.json-this.targetisnull, so anynpm installcrashes mid-reifywith an unhandledTypeErrorinstead of a clean, actionable error.Steps to reproduce
vlt install(orbun install).npm install.Actual
Expected
A clean error (e.g. "node_modules was not installed by npm"), not an unhandled
TypeError. Minimal fix: guard the link branch, e.g.return node.isLink && !!this.target && this.target.matches(node.target).Alternatively, regenerate the hidden lockfile whenever it's absent.
Environment
npm 11.13.0, Node 26 (also reproduced on Node 22/24 in CI).