skip to content

Why does the Angular CLI refuse to update `@angular/core` from v20 straight to v22, and how do you move an app across several majors?

level: middleimportance: must knowfreq 62%

answer

  1. migrations assume the previous major
  2. error names the next major to run
  3. @^21, then @^22
  4. build and test between steps
  5. the update guide's before/during/after steps

basics

~10 s

Migrations and deprecation windows assume one major at a time, so ng update refuses bigger jumps for @angular/* packages. Run ng update @angular/core@^21 @angular/cli@^21, build, test and commit, then repeat for ^22.

solid answer

~50 s

Angular guarantees update support only from the previous major: its release docs say to update across several majors one major at a time. The CLI enforces this for `@angular/*` packages. Asking to go from 20 to 22 fails with `Updating multiple major versions of '@angular/core' at once is not supported`, and the message tells you to run `ng update @angular/core@21` first. The reason is that migrations for 22 assume code that 21's migrations already produced, and APIs deprecated in 21 are removed in 22. The workflow: open the Angular Update Guide for 20 to 21 and do its 'before' steps; run `ng update @angular/core@^21 @angular/cli@^21` plus any third-party Angular libraries; fix the build, run the tests and commit; then repeat for 21 to 22. The caret form lands on the latest patch of each major, which the CLI's own help recommends.

code

bash · 6 lines
bash
# from v20 to v22, one major per step
ng update @angular/core@^21 @angular/cli@^21
ng build && ng test && git commit -am "chore: update to Angular 21"

ng update @angular/core@^22 @angular/cli@^22
ng build && ng test && git commit -am "chore: update to Angular 22"

go deeper

for a junior

Know that Angular is updated one major version at a time with ng update, and that you build and test after each step.

for a middle

Explain why the CLI enforces one major per step, read its error message, and use the update guide's before/during/after steps.

for a senior

Plan a multi-major upgrade: toolchain floors, third-party readiness, verification and commits per step, and how to bisect a regression.

for a principal

Keep the organisation close enough to current that multi-major catch-ups are rare, and budget upgrade work as routine rather than emergency.

## The rule and where it comes from Angular's release documentation states the support boundary plainly. You can `ng update` to a supported version when the version you update *from* is within one major of the version you update *to*. To cross several majors, do one major at a time: 20 to 21, then 21 to 22. The CLI enforces it for packages matching `@angular/*`. If the installed major and the target major differ by more than one, `ng update` stops before changing anything: ```text Updating multiple major versions of '@angular/core' at once is not supported. Please migrate each major version individually. Run 'ng update @angular/core@21' in your workspace directory to update to latest '21.x' version of '@angular/core'. ``` The message links to the update guide for that exact step. `ng update` with no arguments applies the same rule when it suggests commands: for an Angular package two majors behind it offers the next major, not the latest. ## Why one major at a time 1. **Migrations are written against the previous major.** A v22 migration expects code already shaped by the v21 migrations. Skipping 21 means its migrations never run and 22's can misfire. 2. **Deprecations have a one-major window.** Angular keeps a deprecated API for at least one major before removing it. Stepping through each major lets you see the deprecation, and its migration, before the removal lands. 3. **Smaller diffs are reviewable.** Each step produces a bounded change you can build, test and bisect. ## A multi-major workflow 1. **Prepare.** Start from a green build on the current version and a clean git tree. Update to the latest minor and patch of your current major first. A deprecation can be announced in any release, including a minor, so this surfaces warnings while the old API still works. 2. **Read the Update Guide** at angular.dev/update-guide. Choose from/to versions and application complexity (Basic, Medium or Advanced) and tick options such as Angular Material or Windows. It lists steps **before**, **during** and **after** the update. 3. **Step one major:** - `ng update @angular/core@^21 @angular/cli@^21` - add Angular-ecosystem libraries that ship migrations to the same command, for example `@angular/material@^21` 4. **Stabilise.** Fix compilation errors, run unit and end-to-end tests, and read the migration log for manual follow-ups. Commit, or use `--create-commits` to get one commit per migration. 5. **Repeat** for the next major. | Step | Command | Verify | |---|---|---| | 20 → 21 | `ng update @angular/core@^21 @angular/cli@^21` | build, tests, migration log | | 21 → 22 | `ng update @angular/core@^22 @angular/cli@^22` | build, tests, migration log | ## Choosing the version specifier | You type | What the CLI resolves | |---|---| | `ng update @angular/core` | The `latest` tag, refused if that is more than one major ahead | | `ng update @angular/core@21` | The newest 21.x release | | `ng update @angular/core@^21` | The newest 21.x release (the form the CLI's help recommends) | | `ng update @angular/[email protected]` | Exactly 21.0.0, missing later fixes | | `ng update @angular/core --next` | The `next` tag: a prerelease such as a release candidate | When you name an Angular package with a version, the CLI also runs the update with a temporary `@angular/cli` of that major if the installed CLI is not already that major's newest release. The migrations therefore execute with the tooling they were written for, even when the workspace's own CLI is still on the older major. ## Traps along the way - **Node.js and TypeScript floors change.** Angular CLI 22 dropped Node.js 20 and requires Node.js 22.22 or 24.13.1 at minimum, and v22 requires TypeScript 6.0 or later. Upgrade the toolchain as part of the step that needs it. - **Third-party libraries lag.** A library whose peer range does not include the next major blocks the step. Check the libraries before starting. - **Pinning an exact old patch** (`@21.0.0`) misses fixes; the caret form picks the newest `21.x`. - **Doing several steps in one branch without verifying** hides which step broke what. ## What this question is not about How often majors ship and how long each is supported is a release-policy topic. What each framework migration changes in your code belongs to the framework's modernisation topics. Here the point is the mechanism: the CLI's one-major rule and the step-by-step routine it forces.

  • Does the one-major rule apply to third-party libraries too?
    The CLI enforces it only for `@angular/*` packages. A third-party library can jump several of its own majors in one `ng update`, and its migrations for every version in between run in one go. The library's own release notes decide whether that is wise.
  • Why update to the latest minor of your current major before stepping up?
    Angular's deprecation policy allows a deprecation to be announced in any release, including minors. Updating within the major first surfaces those warnings while the old API still works, so the major step has fewer surprises. It also gives a known-good baseline to compare against if the major step breaks something.

saying these in an interview costs you the question

  • Edits package.json to the latest major to skip the intermediate steps
  • Thinks --force bypasses the multiple-majors error
  • Believes skipping a major is fine because the latest migrations include older ones
  • Pins exact .0.0 versions instead of taking the latest patch of each major
  • Chains several major updates without building and testing in between