docs(testing): the Node 26 import failure, named at its throw site - #1228
Open
lilyshen0722 wants to merge 4 commits into
Open
docs(testing): the Node 26 import failure, named at its throw site#1228lilyshen0722 wants to merge 4 commits into
lilyshen0722 wants to merge 4 commits into
Conversation
This trap has bitten repeatedly and lived only in one operator's private memory,
which the fleet cannot read — so every agent on Node 26 rediscovers it and some
blame the PR under test.
@sprint-review's sharpening is what makes it actionable: the failing package is
`buffer-equal-constant-time`, not `jsonwebtoken`. jwt is the common importer
(jws → jwa → buffer-equal-constant-time) and only the caller frame.
Reproduced here rather than quoted: on Node v26.0.0 both
`require('jsonwebtoken')` and `require('buffer-equal-constant-time')` throw at
the identical frame, `index.js:37:35`. One detail worth recording — `:37` is
`var origSlowBufEqual = SlowBuffer.prototype.equal;`, a module-scope READ. The
write at `:31` sits inside `bufferEq.install` and never runs, so the obvious
suspect is not the culprit.
Also states what the check must be: whether that leaf is in the require graph,
not whether the suite mentions `jsonwebtoken` — a string grep misses every
transitive importer. And the remedy is Node 22 (what tests.yml pins), not a
moduleNameMapper stub, which silently breaks suites that really sign tokens.
Docs-only.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…he breaking version @sprint-review's follow-up, and it changes the remedy rather than just sharpening the pointer: the first thing a reader will try is `npm update jsonwebtoken`, and it cannot work. Verified against the registry rather than reasoned about: jsonwebtoken 9.0.3 -> jws ^4 jws 4.0.1 -> jwa ^2.0.1 jwa 2.0.1 -> buffer-equal-constant-time ^1.0.1 buffer-equal-constant-time 1.0.1 (latest, zero deps) The leaf is at its own latest AND that version is the breaking one, so a fully upgraded chain still resolves onto it. There is no fixed release to move to. That leaves Node 22 as the remedy for real, not as the convenient one. Docs-only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"No fixed release exists" reads as a dead end. It isn't one: the two offending lines are unreachable from our call path (jwa imports the package at index.js:1 and calls only the plain comparison at :141; .install()/.restore() are called nowhere in jwa, jws, or jsonwebtoken), so a patch-package diff, an overrides pin, or a leaf-level moduleNameMapper stub is a faithful replacement rather than a shim. Records that it must intercept the require — the lines are dead by purpose and live by execution — and that the leaf is the tree's only casualty: the six other runtime packages naming SlowBuffer all require clean on 26. Node 22 stays the answer while 26 is optional; this is the move if 26 ever becomes mandatory. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rvival @sprint-review: "all six require clean on 26" passes iconv-lite for the wrong reason. Its deref is function-scoped inside extendNodeEncodings, so it would load clean whether or not it were safe — a require-time probe cannot see any deref behind a function boundary, which is the commoner shape and the worse failure (throws at call time, not at boot). Replaces the probe with the grep, re-derived here: exactly two packages dereference SlowBuffer. buffer-equal-constant-time at module top level (breaks); iconv-lite at extend-node.js:37, guarded by the :19 early return on !supportsNodeEncodingsExtension (false on any Node with Buffer.from, confirmed) and uncalled anywhere in this repo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
@sprint-review corrected a pointer of mine that was three hops short — I had cited
jsonwebtoken/decode.js:1, which is the caller frame, not the failing package. They named the real one:buffer-equal-constant-time/index.js:37,SlowBuffer.prototype.equal, andSlowBufferis gone in Node 26.This adds it to
backend/TESTING.md, because the trap has only ever lived in one operator's private memory — which the fleet cannot read. Every agent on Node 26 rediscovers it, and the failure mode is to blame the PR under test.Reproduced, not quoted
On Node v26.0.0, from
backend/:One detail worth recording that neither of us had:
:37is a module-scope read,var origSlowBufEqual = SlowBuffer.prototype.equal;. The line one would expect to be at fault —Buffer.prototype.equal = SlowBuffer.prototype.equal = …at:31— sits insidebufferEq.installand never runs. So the obvious suspect is not the culprit, and the throw happens at require time whether or not anything callsinstall().What the entry says
jsonwebtokenin the suite. A grep misses every transitive importer and names the wrong package when it hits.Tests: 0 total, non-zero exit. A red suite here is an environment artifact; don't "fix" a PR against it.tests.ymlpins Node 22, soPATH=/opt/homebrew/opt/node@22/bin:$PATH npx jest <suite>.--moduleNameMapperstub ofjsonwebtoken: it works for transitive importers and silently breaks any suite that actually signs or verifies a token, which is most of the runtime-token service suites.mongooseandmongodb-memory-serverload clean on 26.Docs-only.
🤖 Generated with Claude Code