fix(arborist): validate peerOptional conflicts in no-save mutations#9641
Merged
Conversation
…9605) Arborist currently treats `save=false` as a signal that invalid `peerOptional` edges from the lockfile can be trusted and skipped. That is correct for non-mutating lockfile reads such as `npm ci`, but it is wrong for commands that still mutate the ideal tree and write a new lockfile while leaving `package.json` unchanged. For example, `npm update` runs with `save=false`, but it can still update package versions in `package-lock.json`. If one of those updates introduces or exposes an invalid optional peer relationship, Arborist can finish successfully and write a lockfile that a later plain `npm install` rejects with `ERESOLVE`. This separates two concepts that were previously conflated: - `#requestedTreeMutation`: whether the command explicitly requested an add, remove, or update operation. - `#mutateTree`: whether the ideal-tree build has actually changed the tree during placement. Invalid `peerOptional` policy now uses `#requestedTreeMutation` instead of `#mutateTree`: - `save=false` + explicit add/rm/update: invalid `peerOptional` edges are treated as resolver problems. - `save=false` + no requested mutation: invalid `peerOptional` edges remain trusted, preserving the existing `npm ci`-style behavior. The requeue path also uses `#requestedTreeMutation`, so a `save=false` update can reprocess an already-seen node when a later placement invalidates one of its `peerOptional` edges. This fixes bad lockfile output for `save=false` commands that mutate the dependency graph, including: - `npm update` - `npm install <pkg> --no-save` - other explicit add/rm/update Arborist builds with `save=false` Those commands should not silently produce an ideal tree or lockfile containing invalid `peerOptional` edges that a subsequent install rejects. Non-mutating `save=false` builds continue to trust the lockfile. In those cases, an invalid `peerOptional` edge is still ignored rather than forcing re-resolution, preserving the behavior needed by `npm ci`-style lockfile reads. Fixes #9604. Added regression coverage for: - `npm update`-style `save=false` mutation rejecting an invalid `peerOptional` graph instead of writing a bad lockfile. - `npm install <pkg> --no-save`-style mutation rejecting the same class of invalid graph. - `save=false` update requeueing an already-seen node when later placement invalidates its `peerOptional` edge. - non-mutating `save=false` builds continuing to ignore invalid `peerOptional` problem edges. Validated with: ```sh git diff --check npm exec -- tap # from workspaces/arborist npm run lint --workspace=@npmcli/arborist ``` Co-authored-by: Dale Lakes <6843636+spitfire55@users.noreply.github.com> (cherry picked from commit 2aa1c7c)
kchindam-infy
approved these changes
Jun 24, 2026
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Backport of #9605 to
release/v11.