fix(npm): allow Node 22 / npm 11 installs (#76486) - #76499
Conversation
OutThisLife
left a comment
There was a problem hiding this comment.
Right diagnosis and the right floor. >=11.17.0 is exactly where min-release-age-exclude lands — I confirmed against the published tarballs rather than the changelog:
| npm | files mentioning min-release-age-exclude |
|---|---|
| 11.16.0 | 0 |
| 11.17.0 | 26 |
| 12.0.2 | 27 |
So the 11.10–11.16 band 3975e9d75 was guarding against stays excluded, and the range becomes satisfiable on a stock toolchain again. Reproduced the break and the fix on the real manifest with engine-strict=true:
# main today, managed Node 22 tree (npm 10.9.8)
npm error notsup Required: {"node":">=20.0.0","npm":">=12.0.0"}
npm error notsup Actual: {"npm":"10.9.8","node":"v22.22.3"}
# with this PR, on a Node 26 tree (npm 11.17.0)
added 208 packages in 658ms
Worth knowing: >=12.0.0 is unsatisfiable on every Node release, not just the stock ones — npm 12 declares node: ^22.22.2 || ^24.15.0 || >=26.0.0, so on a managed tree older than 22.22.2 even the automatic recovery from #76464 can't install it and the update dead-ends. Your change fixes that class too.
One overlap to flag: #76459 moves the same two lines to node >=26.0.0 / npm >=12.0.0. The pairing that actually installs clean is node >=26.0.0 + your npm >=11.17.0 — I've asked there to take your floor, so whichever merges second just needs the other's value. Catching the stale website/package.json mirror was a good call.
OutThisLife
left a comment
There was a problem hiding this comment.
Right diagnosis and the right floor. >=11.17.0 is exactly where min-release-age-exclude lands — I confirmed against the published tarballs rather than the changelog:
| npm | files mentioning min-release-age-exclude |
|---|---|
| 11.16.0 | 0 |
| 11.17.0 | 26 |
| 12.0.2 | 27 |
So the 11.10–11.16 band 3975e9d75 was guarding against stays excluded, and the range becomes satisfiable on a stock toolchain again. Reproduced the break and the fix on the real manifest with engine-strict=true:
# main today, managed Node 22 tree (npm 10.9.8)
npm error notsup Required: {"node":">=20.0.0","npm":">=12.0.0"}
npm error notsup Actual: {"npm":"10.9.8","node":"v22.22.3"}
# with this PR, on a Node 26 tree (npm 11.17.0)
added 208 packages in 658ms
Worth knowing: >=12.0.0 is unsatisfiable on every Node release, not just the stock ones — npm 12 declares node: ^22.22.2 || ^24.15.0 || >=26.0.0, so on a managed tree older than 22.22.2 even the automatic recovery from #76464 can't install it and the update dead-ends. Your change fixes that class too.
One overlap to flag: #76459 moves the same two lines to node >=26.0.0 / npm >=12.0.0. The pairing that actually installs clean is node >=26.0.0 + your npm >=11.17.0 — I've asked there to take your floor, so whichever merges second just needs the other's value. Catching the stale website/package.json mirror was a good call.
|
Thanks — this is a focused correction to the npm engine floor. GitHub current main requires
Automated hermes-sweeper review. |
|
Superseded by #76459. Your diagnosis was right and the What changed is the other side of the constraint. #76459 now installs npm 12 into the vendored Node tree itself, on both POSIX and Windows, so the toolchain satisfies That fixes #76486 for the same users, so this one is redundant rather than wrong. Thanks for catching the stale |
#76499 landed the npm floor as >=11.17.0 on a Node >=20 baseline. This branch takes the other half of the same constraint: the vendored Node 26 tree now installs npm 12 into itself, so the toolchain satisfies the stricter floor rather than the manifest relaxing to meet the tarball. Resolved package.json + package-lock.json to node >=26.0.0 / npm >=12.0.0 and refreshed npm_engine.py's illustrative range to match. website/'s mirror keeps #76499's >=11.17.0 — it is not a root workspace and builds on its own Node.
…ines churn (#76627) The Playwright suite fails identically on every PR regardless of diff (verified on a Python-only PR and a docs-only PR): the mock-backend Electron window never gets a title, so boot/chat/setup/interim specs all fail; only the dead-backend boot-failure path still passes. Breakage window matches the Aug 1 night engines/npm churn (#76499/#76562/#76575). Gated with 'false &&' in the job condition — delete that to re-enable. Root-fix + re-enable tracked in #76627 (Ari).
NousResearch#76499 landed the npm floor as >=11.17.0 on a Node >=20 baseline. This branch takes the other half of the same constraint: the vendored Node 26 tree now installs npm 12 into itself, so the toolchain satisfies the stricter floor rather than the manifest relaxing to meet the tarball. Resolved package.json + package-lock.json to node >=26.0.0 / npm >=12.0.0 and refreshed npm_engine.py's illustrative range to match. website/'s mirror keeps NousResearch#76499's >=11.17.0 — it is not a root workspace and builds on its own Node.
…ines churn (NousResearch#76627) The Playwright suite fails identically on every PR regardless of diff (verified on a Python-only PR and a docs-only PR): the mock-backend Electron window never gets a title, so boot/chat/setup/interim specs all fail; only the dead-backend boot-failure path still passes. Breakage window matches the Aug 1 night engines/npm churn (NousResearch#76499/NousResearch#76562/NousResearch#76575). Gated with 'false &&' in the job condition — delete that to re-enable. Root-fix + re-enable tracked in NousResearch#76627 (Ari).
NousResearch#76499 landed the npm floor as >=11.17.0 on a Node >=20 baseline. This branch takes the other half of the same constraint: the vendored Node 26 tree now installs npm 12 into itself, so the toolchain satisfies the stricter floor rather than the manifest relaxing to meet the tarball. Resolved package.json + package-lock.json to node >=26.0.0 / npm >=12.0.0 and refreshed npm_engine.py's illustrative range to match. website/'s mirror keeps NousResearch#76499's >=11.17.0 — it is not a root workspace and builds on its own Node.
Closes #76486
Problem
Since commit
f88ed6c71("fix: fix @nousresearch/ui version, update to npm 12"), the rootpackage.jsondeclares"npm": ">=12.0.0". npm 11 is the stable line bundled with Node 22 (e.g. npm 11.16.0 ships with Node 22.22.2), and npm 11.x is still actively maintained (11.19.0 is the latest 11.x). Withengine-strict=truein.npmrc, everynpm install/npm cion a stock Node 22 + npm 11 setup fails withEBADENGINE, andhermes updatereports a partial/unclean dependency refresh.Root cause
>=12.0.0is unsatisfiable on a stock Node 22 install — there is no npm 12 bundled with Node, and npm 11 is the current stable major line. The previous range (<11.10.0 || >=12.0.0, added in3975e9d75) already handled the important constraint: npm 11.10.0–11.16.x implementsmin-release-agebut notmin-release-age-exclude, so those versions would silently ignore the exclusion list in.npmrc(min-release-age=14+ excludes for fast-moving packages like@assistant-ui/*,@radix-ui/*,vite,rolldown, etc.) and block installs of any package released within the last 14 days. That is the "bad band" that must stay excluded.min-release-age-excludewas added to npm CLI on 2026-06-10 (npm/cli commitc3e1a7175c) and first shipped in npm 11.17.0 (2026-06-11), which I verified by inspecting the published tarballs:min-release-age-excludesupportFix
Relax the range to
>=11.17.0in the rootpackage.json, itspackage-lock.jsonmirror, and the mirroredwebsite/package.json(which still had the stale<11.10.0 || >=12.0.0).>=11.17.0:min-release-age+min-release-age-excludesupport (11.17.x, 11.18.x, 11.19.x) is accepted, as is npm 12+.EBADENGINE(they would break installs by ignoring the.npmrcexclusion list), and old npm 10.x still fails so the EBADENGINE auto-repair inhermes_cli/npm_engine.pyupgrades the managed npm instead of leaving users stuck on a stale npm with the eternal "new major version" notice.npm_engine.pyrepair already parses the required range out of the EBADENGINE error text, so no changes to the repair logic or its tests were needed (tests are range-agnostic by design — all 20 pass unchanged).Changes
package.json—engines.npm:>=12.0.0→>=11.17.0package-lock.json— root engines mirror:>=12.0.0→>=11.17.0website/package.json— engines mirror:<11.10.0 || >=12.0.0→>=11.17.0(kept in sync with root; its lockfile root entry only pinsnode, no change needed)hermes_cli/npm_engine.py— docstring example updated to the new range (documentation only)Verification
python3 -m pytest tests/hermes_cli/test_npm_engine.py -q→ 20 passednpm_engine.pycompiles cleanly.