fix(js): guard against undefined closest node in rehoistNodes#34347
Merged
leosvelperez merged 1 commit intonrwl:masterfrom Feb 25, 2026
Merged
Conversation
✅ Deploy Preview for nx-dev canceled.
|
✅ Deploy Preview for nx-docs canceled.
|
Contributor
| I'm not making any claims about the quality of their work, but I wanted to let you know that @kaigritun is a fully-autonomous non-human actor |
Contributor
| View your CI Pipeline Execution ↗ for commit b6a30d2
☁️ Nx Cloud last updated this comment at |
leosvelperez approved these changes Feb 25, 2026
When transitive dependencies have multiple versions where neither version is reachable from a direct dependency in package.json, pathLengthToIncoming() returns undefined for all nested nodes. This leaves 'closest' undefined, causing switchNodeToHoisted() to crash with: 'Cannot read properties of undefined (reading "name")' This fix adds a guard to only call switchNodeToHoisted() when a closest node was actually found. Fixes nrwl#34322
e859289 to b6a30d2 Compare FrozenPandaz pushed a commit that referenced this pull request Feb 26, 2026
## Current Behavior When running `@nx/js:prune-lockfile` on a monorepo with transitive dependencies that have multiple versions where neither version is reachable from a direct dependency in package.json, the executor throws: ``` NX An error occurred while creating pruned lockfile Original error: Cannot read properties of undefined (reading 'name') TypeError: Cannot read properties of undefined (reading 'name') at switchNodeToHoisted (node_modules/nx/src/plugins/js/lock-file/project-graph-pruning.js:165:31) ``` ## Expected Behavior The lockfile pruning should complete without crashing, even when some transitive dependencies cannot be traced back to a direct dependency. ## Root Cause In `rehoistNodes()`, when there are multiple nested nodes for a package, the code finds the "closest" node by computing `pathLengthToIncoming()` for each. However, when none of the nested nodes have a path to any direct dependency in package.json, `pathLengthToIncoming()` returns `undefined` for all of them. Since `undefined < Infinity` is `false` in JavaScript, `closest` remains `undefined`, and then `switchNodeToHoisted(undefined, ...)` crashes. ## Fix Add a guard to only call `switchNodeToHoisted()` when a closest node was actually found: ```typescript if (closest) { switchNodeToHoisted(closest, builder, invBuilder); } ``` This allows the pruning to continue - the nested nodes simply won't be rehoisted if no closest node can be determined. ## Related Issue Fixes #34322 ## Test Added Added a unit test that verifies `rehoistNodes()` doesn't crash when nested nodes have no path to package.json dependencies. (cherry picked from commit b46de60) FrozenPandaz pushed a commit that referenced this pull request Feb 26, 2026
## Current Behavior When running `@nx/js:prune-lockfile` on a monorepo with transitive dependencies that have multiple versions where neither version is reachable from a direct dependency in package.json, the executor throws: ``` NX An error occurred while creating pruned lockfile Original error: Cannot read properties of undefined (reading 'name') TypeError: Cannot read properties of undefined (reading 'name') at switchNodeToHoisted (node_modules/nx/src/plugins/js/lock-file/project-graph-pruning.js:165:31) ``` ## Expected Behavior The lockfile pruning should complete without crashing, even when some transitive dependencies cannot be traced back to a direct dependency. ## Root Cause In `rehoistNodes()`, when there are multiple nested nodes for a package, the code finds the "closest" node by computing `pathLengthToIncoming()` for each. However, when none of the nested nodes have a path to any direct dependency in package.json, `pathLengthToIncoming()` returns `undefined` for all of them. Since `undefined < Infinity` is `false` in JavaScript, `closest` remains `undefined`, and then `switchNodeToHoisted(undefined, ...)` crashes. ## Fix Add a guard to only call `switchNodeToHoisted()` when a closest node was actually found: ```typescript if (closest) { switchNodeToHoisted(closest, builder, invBuilder); } ``` This allows the pruning to continue - the nested nodes simply won't be rehoisted if no closest node can be determined. ## Related Issue Fixes #34322 ## Test Added Added a unit test that verifies `rehoistNodes()` doesn't crash when nested nodes have no path to package.json dependencies. (cherry picked from commit b46de60) Contributor
| This pull request has already been merged/closed. If you experience issues related to these changes, please open a new issue referencing this pull request. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Current Behavior
When running
@nx/js:prune-lockfileon a monorepo with transitive dependencies that have multiple versions where neither version is reachable from a direct dependency in package.json, the executor throws:Expected Behavior
The lockfile pruning should complete without crashing, even when some transitive dependencies cannot be traced back to a direct dependency.
Root Cause
In
rehoistNodes(), when there are multiple nested nodes for a package, the code finds the "closest" node by computingpathLengthToIncoming()for each. However, when none of the nested nodes have a path to any direct dependency in package.json,pathLengthToIncoming()returnsundefinedfor all of them. Sinceundefined < Infinityisfalsein JavaScript,closestremainsundefined, and thenswitchNodeToHoisted(undefined, ...)crashes.Fix
Add a guard to only call
switchNodeToHoisted()when a closest node was actually found:This allows the pruning to continue - the nested nodes simply won't be rehoisted if no closest node can be determined.
Related Issue
Fixes #34322
Test Added
Added a unit test that verifies
rehoistNodes()doesn't crash when nested nodes have no path to package.json dependencies.