SIGN IN SIGN UP

fix(arborist): avoid crash when peer back-off detaches a node (#9770)

Installing a package whose peers form a cycle with an already-installed
optional peer could crash with `TypeError: Cannot read properties of
null (reading 'explain')` instead of resolving or reporting a real
conflict.
A minimal trigger: `vite@8.1.4` declares an optional peer on
`@vitejs/devtools`, `@vitejs/devtools` peers back on `vite`, so
installing vite and then adding devtools crashes.

The root cause is in `#loadPeerSet`.
While resolving a package's peer edge through the parent's edge, the
recursive `#nodeFromEdge` call can place a compatible peer that replaces
and detaches the current node from the tree mid-iteration.
The now-invalid edge then reached `#failPeerConflict`, whose
`#explainPeerConflict` calls `node.resolve(edge.name).explain()` on the
detached node.
`resolve()` returns `null` for a node no longer in the tree, so
`.explain()` threw.

A detached node has been superseded by a compatible peer, so there is no
real conflict to report.
The fix adds a guard that stops processing when the node has been
detached, right before `#failPeerConflict`, mirroring the existing
top-of-loop detachment check.
This lets the install complete by keeping the compatible peer that
replaced the node (for the reproduction, `@vitejs/devtools` backs off to
a version that satisfies vite's optional peer range) rather than
crashing or raising a spurious `ERESOLVE`.

## References

Fixes #5222
Closes #4787
M
Manzoor Wani committed
74eff59db40b4aff9665f4083b475a000bb46e50
Parent: 882d0b2
Committed by GitHub <noreply@github.com> on 7/27/2026, 8:14:28 PM