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