Skip to content

Runtime Node guard accepts releases excluded by package engines #254

Description

@MicroMilo

Summary

package.json supports only Node ^20.19.0, ^22.13.0, or >=24, while the runtime guard and its tests accept every major version >=20, including earlier 20/22 releases and all Node 21/23 releases.

Impact: On Node 20.0–20.18, Node 21, Node 22.0–22.12, or Node 23, shouldRejectNodeVersion returns false. The entrypoint therefore omits its promised early actionable failure and continues on a runtime the package and documentation declare unsupported, potentially failing later with the cryptic runtime errors the guard was introduced to prevent.

Code path

Checked against current main at commit 60d55e47883e924ff1a5f0ab1fe67a25bc3b0877.

  • package.json:30-32
  • src/version-guard.ts:13-14
  • src/version-guard.ts:38-40
  • src/version-guard.test.ts:31-40
  • src/index.ts:25-31

Steps to reproduce

Validation level: current HEAD supplemental dynamic witness reproduced.

node --experimental-strip-types --input-type=module -e "import { shouldRejectNodeVersion } from './src/version-guard.ts'; const versions=['20.0.0','20.18.0','20.19.0','21.9.0','22.12.0','22.13.0','23.9.0','24.0.0']; console.log(JSON.stringify(Object.fromEntries(versions.map(v=>[v,shouldRejectNodeVersion(v)]))));"

Observed output:

{"20.0.0":false,"20.18.0":false,"20.19.0":false,"21.9.0":false,"22.12.0":false,"22.13.0":false,"23.9.0":false,"24.0.0":false}

Neighboring control:

node --experimental-strip-types --input-type=module -e "import { shouldRejectNodeVersion } from './src/version-guard.ts'; console.log(JSON.stringify({below_documented_floor:shouldRejectNodeVersion('18.20.0'),documented_20_floor:shouldRejectNodeVersion('20.19.0'),documented_22_floor:shouldRejectNodeVersion('22.13.0'),documented_24_floor:shouldRejectNodeVersion('24.0.0')}));"

Control output:

{"below_documented_floor":true,"documented_20_floor":false,"documented_22_floor":false,"documented_24_floor":false}

Expected behavior

The documented/source-grounded contract should hold without the drift described above.

Actual behavior

The major-only runtime guard accepts Node releases excluded by the declared semver range and causes doctor to report them as supported.

Existing coverage

I checked the current issue and PR lists for overlapping titles/root-cause keywords before filing this. I did not find an item covering this same root cause.

Suggested fix

Make the shared startup/doctor guard enforce the full package engine range, then update its messages and tests.

Suggested tests

  • Add a regression test for the reproduction above.
  • Add a neighboring control assertion so the intended non-bug path remains covered.

Submitted with Codex.

Metadata

Metadata

Assignees

Labels

in-progressAssigned and actively being worked on

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions