Skip to content

fix(arborist): preserve failed targets while loading peer sets - #10150

Open
drewmt wants to merge 2 commits into
npm:latestfrom
drewmt:fix/failed-peer-load-loop
Open

drewmt wants to merge 2 commits into
npm:latestfrom
drewmt:fix/failed-peer-load-loop

Conversation

@drewmt

@drewmt drewmt commented Oct 21, 2026

Copy link
Copy Markdown

An unavailable direct dependency can be repeatedly replaced by a valid version requested by a transitive peer. Its original edge then reinstalls the failed, versionless node, so npm install --package-lock-only never reaches the load-error reporting phase.

Keep an already failed target in the real tree while loading subsequent peer sets. The existing pruning phase can then report its original ETARGET, or mark the optional peer-dependent group inert. Valid peer targets continue through the existing resolution path. This does not add an iteration limit or change dependency versions.

The regression includes the transitive react-shallow-renderer peer from the report, with required and optional dependencies under default, strict and force modes.

Validation

  • On unmodified 12.2.1, the reported CLI reproduction did not terminate within 20 seconds; the one-peer control returned ETARGET in 394 ms. The mock-registry regression also timed out before the fix.
  • After the change, the reproduction returns the original ETARGET in 671 ms on Node 26.8.1 and 584 ms on Node 24.19.0. The valid-version control succeeds.
  • Full Arborist npm test: all 59 test files pass with 100% statement, branch, function and line coverage, including lint and package template checks (Node 24.19.0). No upstream CI result is claimed here.
  • Root CLI tests: 6027 passing, 22 skipped, 100% coverage. The command's postlint template checker fails on workflow branch-template differences also reproduced on clean base ae98c62586b6fea44114f72e249e246a2981de70; no tooling changes are included.

References

Fixes #10149

@drewmt
drewmt requested a review from a team as a code owner October 21, 2026 11:16
@martinrrm
martinrrm requested a balanced review from Copilot November 3, 2026 20:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The guard blocks a safely nestable peer, causing a required dependency to be hoisted and turn a failed optional dependency into an ETARGET install failure.

1 open issue
What changed in this PR

This PR addresses #10149 by preserving failed dependency targets during Arborist peer-set loading, preventing repeated replacements that can make installation loop indefinitely.

Changes:

  • Skip peer resolution when the existing target is a recorded load failure.
  • Add regression coverage for multiple peer dependents, including a transitive peer, across required and optional dependencies in default, strict, and force modes.
File Description
workspaces/​arborist/​test/​arborist/​build-ideal-tree.js Tests load-error reporting and optional-group handling without looping.
workspaces/​arborist/​lib/​arborist/​build-ideal-tree.js Adds a guard to preserve failed targets during peer-set loading.

Review effort: Balanced

Comment on lines +1622 to +1624
const existing = node.parent.sourceReference.resolve(edge.name)
if (this.#loadFailures.has(existing)) {
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a really

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add this mixed required/optional case to the regression tests, asserting both the valid nested peer and the inert optional ancestor across those modes, while retaining the new loop tests.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 1697836. The regression covers default, strict, and force modes: the required peer resolves beneath the wrapper, and the failed optional root dependency stays inert. The existing loop tests are unchanged. Both the Arborist and npm CLI suites passed locally.

@martinrrm martinrrm self-assigned this Nov 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] npm install never finishes when a dependency has no matching version and two packages peer on it (11.21.1, 12.2.1)

4 participants