Deprecating npm Package Versions
npm deprecate attaches a message to one version, a range of versions or a whole package. From then on, every install that resolves a deprecated version prints the message — in developer terminals, CI logs and dependency bot pull requests — while the version stays installable, so no existing build breaks. It is the right tool for broken releases, vulnerable versions, retired major lines and abandoned packages, and it is reversible — which makes it the default lifecycle action whenever you are unsure. This guide covers the command and its version-range syntax, what users actually see, how to write messages that get acted on, and how to automate deprecation as part of releases.
What deprecation does and does not do
Deprecation sets a deprecated field on the selected versions in the registry's metadata. Package managers read it during resolution and print a warning:
npm warn deprecated @acme/sdk@4.2.0: Security issue CVE-2026-1234 fixed in 4.2.1 and 5.0.3. Upgrade with: npm i @acme/sdk@^4.2.1
WARN deprecated @acme/sdk@4.2.0: Security issue CVE-2026-1234 fixed in 4.2.1 and 5.0.3. Upgrade with: npm i @acme/sdk@^4.2.1
What it does not do: remove the version, change what ranges resolve to, or stop installs. A consumer with ^4.2.0 and a lockfile pinning 4.2.0 keeps installing 4.2.0 — with a warning — until they update. Deprecation informs; your fixed release is what actually changes resolution. The broader lifecycle is covered in Package Deprecation and Lifecycle.
The command
# One version
npm deprecate @acme/sdk@4.2.0 "Broken ESM entry; use 4.2.1"
# A range (quote it so the shell does not interpret < or >)
npm deprecate @acme/sdk@"<4.2.1" "Security issue CVE-2026-1234 fixed in 4.2.1"
# A whole major line
npm deprecate @acme/sdk@"4.x" "4.x is no longer supported; upgrade to 6.x (see MIGRATION.md)"
# The whole package
npm deprecate @acme/fetch-retry "Merged into @acme/http; see README for migration"
# Undo: an empty message clears deprecation for the selected versions
npm deprecate @acme/sdk@4.2.0 ""
# Check the result
npm view @acme/sdk@4.2.0 deprecated
Deprecation is a write operation: it needs publish rights on the package and, with 2FA required, a one-time password or the trusted CI identity. The range syntax is standard semver, so "<4.2.1", ">=3.0.0 <3.5.2", and "4.x" all work. Prerelease versions are only matched by ranges that include prereleases, so deprecate them explicitly if needed.
Writing messages that work
The message is shown in a single line, often in a crowded CI log, and it is all most users will read. Make it:
- Specific — what is wrong: "Security issue CVE-2026-1234", "Broken CommonJS build", "Published by mistake".
- Actionable — what to do: the fixed version or the replacement package, ideally as a command.
- Short — under about 200 characters, so it survives terminal wrapping.
- Linked where needed — a pointer to a migration guide or advisory for anything that needs more explanation.
Bad: "deprecated", "do not use", "old version". Good: "Broken types for CommonJS consumers; fixed in 3.1.2".
Avoid alarmist wording for routine end-of-support messages. Users learn to ignore warnings that sound urgent but are not, and then miss the ones that are. Reserve words like "security" for actual security issues, and include the advisory identifier so users can judge severity themselves. Review deprecation messages the same way you review release notes — a second pair of eyes catches the missing version number or the ambiguous instruction before thousands of users read it.
Common scenarios
A broken release. Publish the fix first, then deprecate the broken version with a pointer to it. Deprecating before a fix exists leaves users with a warning and nowhere to go.
A vulnerability. Release fixed versions on every supported line, publish an advisory, then deprecate the affected range on each line. The advisory reaches npm audit; the deprecation reaches everyone who installs. Supply-chain context is in Responding to a Compromised Dependency.
End of support. When a major line leaves support, deprecate the whole line with the upgrade guide. Do it on the announced date, not before, so users are not warned about versions you still promised to support.
A mistaken publish. A version published with the wrong tag, missing files or leaked content: deprecate it, and if it is within the unpublish window and meets the policy, consider unpublishing — see Unpublishing a Package Within npm Policy. A leaked secret must be rotated regardless; removing the version does not un-leak it.
Deprecation versus the alternatives
Deprecation is one of several tools for steering users away from a version, and choosing the right one — or the right combination — depends on what you need to change.
In practice, a broken or vulnerable release calls for two actions together: a fixed release, which moves every range-based consumer forward on their next install, and a deprecation, which warns everyone still pinned to the bad version. A wrong latest tag adds a third: moving the tag back. Unpublishing is almost never part of the answer, because it hurts precisely the users who are already exposed.
Deprecating in a monorepo release
Monorepos that publish many packages sometimes need to deprecate a coordinated set — every package in a fixed group at a retired major, or several packages affected by the same vulnerability in a shared dependency. Script it rather than typing commands by hand, so no package is missed and the messages are consistent:
for pkg in @acme/core @acme/react @acme/vue; do
npm deprecate "$pkg@4.x" "4.x reached end of support on 2026-06-30; upgrade to 5.x (see MIGRATION.md)"
echo "$pkg@4: $(npm view "$pkg@4" deprecated | tail -1)"
done
Record the list of packages and ranges in the pull request or release notes that triggered the deprecation, so the action is reviewable and repeatable. If a package in the set has a different support policy — for example, a CLI still supported on 4.x because it runs on older Node.js versions — handle it explicitly rather than excluding it silently.
Automating deprecation
Deprecations tied to releases can run in CI, using the same trusted identity as publishing:
- name: Deprecate versions fixed by this release
if: steps.release.outputs.published == 'true'
run: |
npm deprecate @acme/sdk@"<4.2.1" "Security issue CVE-2026-1234; upgrade to 4.2.1 or 5.0.3"
npm view @acme/sdk@4.2.0 deprecated
For monorepos, a small script can deprecate an end-of-life range across every package in a fixed group, so no package in the group is left out. Print the resulting deprecated field after each command, as above, so the job log shows exactly what changed on the registry.
Worked example: a broken CommonJS build
A library releases 3.1.1, whose CommonJS entry fails with ERR_REQUIRE_ESM because of a build configuration change. Within an hour the maintainers publish 3.1.2 with the fix and run npm deprecate your-lib@3.1.1 "Broken CommonJS entry (ERR_REQUIRE_ESM); fixed in 3.1.2". Users with ^3.1.0 pick up 3.1.2 on their next install; users whose lockfiles pinned 3.1.1 see the message on every install in CI and update. Two weeks later, download statistics show 3.1.1 usage falling to near zero.
Prevention and guardrails
- Always ship the fix before deprecating the broken version.
- Use the smallest correct range, and quote it.
- Write specific, actionable messages with the replacement version.
- Automate lifecycle deprecations in CI, with the trusted publishing identity.
Frequently Asked Questions
Does deprecation affect npm audit?
No. npm audit reads security advisories. Deprecation is a separate signal; use both for security issues.
Can consumers hide deprecation warnings? They can lower the log level, but most do not. The warnings are designed to be noticed, which is why messages should be precise rather than alarming.
Does deprecation work on private registries?
Most npm-compatible registries support it. Verdaccio, Artifactory and GitHub Packages accept npm deprecate; check your registry's documentation.
Do deprecation warnings appear for transitive dependencies? Yes. If any package in the tree resolves to a deprecated version, the warning is printed, even though the consumer never installed it directly. That is why deprecating widely used versions produces many reports; the fix is usually for the intermediate package to update its range.
How quickly does a deprecation take effect? Immediately on the registry. Clients and proxy registries may serve cached metadata for a short time, so some installs within the first minutes may not show the warning.
Can I deprecate a version I do not own? No. Only owners and maintainers with publish rights can deprecate. For a problematic dependency you do not control, report it to its maintainers or, for malicious packages, to the registry.
Related
- Package Deprecation and Lifecycle covers the whole lifecycle.
- Unpublishing a Package Within npm Policy explains when removal is possible instead.
- Fixing a Wrong latest Dist-Tag pairs tag fixes with deprecation.
- Maintaining LTS Branches with Backport Releases defines when lines reach end of support.