skip to content

When `ng update @angular/core@22` stops with 'Incompatible peer dependencies found' because a third-party library only accepts Angular 21, how do you resolve it and when is `--force` acceptable?

level: seniorimportance: should knowfreq 50%

answer

  1. read which package and which range
  2. is there a release for the new major?
  3. name it in the same ng update
  4. --force skips the check, not the risk
  5. core gets one major of leniency, common does not

basics

~20 s

Name a library release that accepts Angular 22 in the same ng update; without one, wait, replace or patch the library. --force only skips the CLI's check, so use it only after proving the library works on 22.

solid answer

~50 s

The CLI validated peer dependencies for the whole plan before installing anything. The log line above the error names the culprit, for example `Package "data-grid" has an incompatible peer dependency to "@angular/common" (requires "^21.0.0", would install "22.2.0")`. Step one is to check whether the library has a release for Angular 22. If it does, run `ng update @angular/core@22 @angular/cli@22 data-grid@<that version>`, so the check uses the library's new peer ranges and its migrations run if it ships any. If not, the choices are to hold the update, replace the library, or patch or fork it. `--force` makes the CLI ignore peer mismatches. It is acceptable only as a deliberate, verified exception: the library is known not to use APIs removed in 22, the build and tests pass, and there is a ticket to remove the override.

code

bash · 7 lines
bash
ng update @angular/core@22 @angular/cli@22
# Package "data-grid" has an incompatible peer dependency to "@angular/common"
#   (requires "^21.0.0", would install "22.2.0").
# Incompatible peer dependencies found. See above for details.

npm view data-grid@6 peerDependencies      # check a candidate release
ng update @angular/core@22 @angular/cli@22 data-grid@6

go deeper

for a junior

Know that ng update checks that installed libraries accept the new Angular version and stops if one does not.

for a middle

Read the incompatible-peer log line, find a compatible library release, and name it in the same ng update command.

for a senior

Choose between waiting, replacing, patching and a verified --force, knowing what --force does not do and why removed APIs make it risky.

for a principal

Set dependency policy so upgrades are not hostage to slow libraries: vetting criteria, owners for internal libraries, and a time limit on forced exceptions.

## What actually failed Before installing anything, `ng update` builds an update plan and validates **peer dependencies** in both directions: - **forward**: every package being updated must accept the versions of its peers in the plan; - **reverse**: every *installed* package must accept the new versions of the packages it peers on. The reverse check is what trips here. A grid library installed at 5.x declares `"@angular/common": "^21.0.0"`, and the plan would install `@angular/common` 22.2.0. The CLI logs one line per conflict, then stops with `Incompatible peer dependencies found. See above for details. You can bypass this check using the --force option.` Nothing has been written or installed yet, so the workspace is untouched. Two details affect how you read the log: - For peer ranges on `@angular/core` itself, the CLI is lenient by one major: it treats `^21.0.0` as also satisfied by 22.x. A range on `@angular/common`, `@angular/forms` or any other package gets no such leniency, so the error usually names one of those. - Peers marked optional in the library's `peerDependenciesMeta` are logged but do not stop the update. ## Resolving it, in order of preference 1. **Find a compatible release.** Check the library's changelog or its published peer ranges for a version that accepts Angular 22. 2. **Update together.** Name it in the same command: ```bash ng update @angular/core@22 @angular/cli@22 data-grid@6 ``` The plan now validates against the library's *new* peer ranges. If the library publishes `ng-update` metadata, its migrations run after the install, just like Angular's. A library does not need `ng-update` metadata to be named; without it, it is simply updated. 3. **Update the library first** if a release supports both majors (say `^21.0.0 || ^22.0.0`). Take it on the current Angular, verify, then run the framework update. 4. **No compatible release yet.** Choose deliberately: - hold the Angular update and track the library's progress; - replace the library with a maintained alternative; - patch or fork it, contributing the fix upstream. ## When `--force` is acceptable `--force` tells the CLI to ignore peer dependency mismatches. It does not make the library compatible. v22 removed APIs such as `ComponentFactoryResolver`, and a library compiled against 21 that uses a removed API will fail at build time or at runtime. Treat `--force` as an exception that needs evidence: | Condition | Why it matters | |---|---| | The library's source or changelog shows no use of APIs removed in 22 | Removed APIs fail at compile time or at runtime | | `ng build` and the full test suite pass after the forced update | Evidence rather than hope | | Its screens are exercised by end-to-end tests or manual checks | Many failures only appear at runtime | | A ticket tracks removing the override when the library catches up | Stops a temporary exception becoming permanent | Your package manager may apply its own peer-dependency rules independently of the CLI's check. On npm 7 and later the CLI already installs with npm's force behaviour during `ng update`, so the CLI's validation is the real gate there. ## What not to do - Edit `package.json` by hand to 22 and run an install. That skips both the peer check and every migration. - Pass `--force` by default in scripts, which silently accepts every future conflict too. - Leave the library at 21-only and ship, trusting that it built. Rendering and dependency injection failures show up at runtime. ## Preventing it next time Before an upgrade, list the Angular-dependent libraries and check their peer ranges against the target major. Prefer libraries that publish updates promptly after Angular majors, and keep internal libraries' peer ranges current, for example `^21.0.0 || ^22.0.0` once they are verified on both.

  • Why might a library with `"@angular/core": "^21.0.0"` pass the check while its `"@angular/common": "^21.0.0"` fails it?
    The CLI widens peer ranges on `@angular/core` by one major before validating, on the assumption that libraries usually survive one major. It applies that only to peers named `@angular/core`, not to other framework packages. So the same `^21` range passes for core and fails for common when you move to 22.
  • Is anything left half-installed when the peer check fails?
    No. The peer check runs while the plan is being resolved, before `package.json` is written or anything is installed. The workspace is unchanged. Even when a later install fails, the CLI restores the original `package.json`.

The peer check is like a venue confirming every performer's contract before the show moves to a bigger stage: one act whose contract names the old venue stops the move. --force is signing the move anyway, and it only works if that act really can perform on the new stage.

saying these in an interview costs you the question

  • Reaches for --force first without reading which package conflicts
  • Believes --force makes a library compatible with the new major
  • Edits package.json manually to bypass ng update's peer check
  • Thinks a library must support ng update to be named in the command
  • Assumes the conflict is the framework's fault rather than the library's peer range