ExploitedPHP2Shell is now on CISA KEV

One in ten popular npm releases needed a hotfix. A cooldown won't skip it.

We replayed six months of releases from 25 widely used npm packages: 630 stable versions published between April 29 and October 28, 2026. 59 of them were followed by a patch on the same line faster than the project’s usual rhythm, most within a few hours. Package managers now hold new versions back by default to stop poisoned releases. That delay does not skip a broken release. It only moves the moment you install it.

Releases replayed
630
Got a hotfix
9.6%
Median time to the fix
5.7 h
Broken versions removed
0 of 59

The fix usually lands within hours

Half of the 59 fixes were out within 5.7 hours of the release they fixed, and 45 within a day. Only one took longer than two days.

30
6
9
13
1
Time from the broken release to the next patch on the same line.

Six of the fixes needed a fix of their own. @supabase/supabase-js went from 2.110.3 to 2.110.7 in three days.

It depends on the project, not on how often it ships

openai published 61 versions in the window and none needed a quick follow-up. @supabase/supabase-js published 36 and 10 did. 12 of the 25 packages had no hotfixes at all.

Share of each package’s releases that got a fast follow-up patch.

No hotfixes: react, typescript, eslint, tailwindcss, prisma, @prisma/client, nuxt, @angular/core, @playwright/test, cypress, stripe, openai.

Patch releases broke more often than minor ones

Patch (x.y.Z)
11.3%
43 of 380 hotfixed
Minor (x.Y.0)
6.3%
14 of 224 hotfixed

A patch is supposed to be the safe upgrade, yet 11.3% of patches got a quick follow-up against 6.3% of minor releases. Fix-of-a-fix chains are not the reason: without them, patches still come out at 11.5%. There were only 9 major releases, too few to compare (2 were hotfixed).

Why a cooldown doesn’t skip them

pnpm now waits a day before it installs a new version (minimumReleaseAge, default 1440 minutes), npm has min-release-age, and since July 2026 Dependabot waits three days before it opens a version-update pull request. These delays work against malicious releases because those usually get pulled from the registry within hours. When the delay ends, the bad version is gone.

Broken releases stay. None of the 59 was deprecated or unpublished. Take a release that got its fix six hours later. With a one-day cooldown, the broken version becomes installable after 24 hours and the fix after 30. For those six hours the cooldown hands you the broken version, just as the registry did on release day.

No cooldown

No cooldown: the broken version is the newest installable version from hour 0 to hour 6.

One-day cooldown (pnpm default)

One-day cooldown (pnpm default): the broken version is the newest installable version from hour 24 to hour 30.

Broken version installableFix installableAn update installs the broken version

What turns the delay into a skip is hindsight. When the cooldown ends, the fix usually exists already, so a rule like “skip a version if a patch on the same line followed it faster than the project’s usual rhythm” can act on it. Combined with a one-day cooldown, that rule would have avoided 45 of the 59 broken releases; combined with Dependabot’s three days, all 59.

That rule is part of every Changes.Watch upgrade verdict: a hotfixed version gets skip and names the version to install instead.

Waiting forever isn’t safe either

152 of the 630 releases (24%) now have security advisories that a newer version fixes, across 8 of the packages. A cooldown is a delay before upgrading, not a reason to stop.

How we counted

All 25 packages as a table
PackageReleasesHotfixedRate
@supabase/supabase-js3610 of 3628%
@nestjs/core328 of 2928%
fastify143 of 1421%
astro6413 of 6221%
storybook275 of 2619%
vue112 of 1118%
react-router222 of 2010%
@anthropic-ai/sdk646 of 649%
next333 of 339%
prettier131 of 138%
@sentry/node463 of 437%
vite302 of 307%
svelte201 of 195%
react140 of 140%
typescript10 of 10%
eslint150 of 150%
tailwindcss40 of 40%
prisma40 of 40%
@prisma/client40 of 40%
nuxt200 of 190%
@angular/core560 of 560%
@playwright/test80 of 80%
cypress160 of 150%
stripe150 of 150%
openai610 of 580%