Skip to content

fix: don't turn an omitted assert message into undefined - #70

Open
hexbinoct wants to merge 1 commit into
nodejs:mainfrom
hexbinoct:hexbinoct/assert-message-forwarding
Open

fix: don't turn an omitted assert message into undefined#70
hexbinoct wants to merge 1 commit into
nodejs:mainfrom
hexbinoct:hexbinoct/assert-message-forwarding

Conversation

@hexbinoct

Copy link
Copy Markdown
Contributor

The Node.js implementor's assert shim swallows the actual/expected diff on Node.js 26, so a
failing test reports a harness error instead of the conformance gap that caused it.

What happens

implementors/node/assert.js forwards each method through an arrow function with named
parameters:

strictEqual: (actual, expected, message) => strictEqual(actual, expected, message),

A caller that omits the message therefore passes an explicit undefined. Node.js 26 changed the
internal message parameter into a variadic tuple (nodejs/node#58849,
first released in v26.0.0), and there [undefined] is a message of the wrong type rather than an
absent one:

TypeError [ERR_INVALID_ARG_TYPE]: The "message" argument must be one of type string or function. Received undefined

So on v26 assert.strictEqual(1, 2) reports that instead of an AssertionError carrying 1 and
2. strictEqual, notStrictEqual and deepStrictEqual take that path; ok, match and
throws still report normally.

Node.js assert.strictEqual(1, 2)
v24.18.1 AssertionError
v25.9.0 AssertionError
v26.0.0 TypeError [ERR_INVALID_ARG_TYPE]
v26.5.1 TypeError [ERR_INVALID_ARG_TYPE]

The suite stays green either way, because nothing is wrong until an assertion actually fails. That
is also why it matters: the failure message is what a conformance run is read for, and a runtime
with a genuine Node-API gap would be handed a message about the CTS harness rather than about its
own behavior.

The change

  • implementors/node/assert.js: forward with rest arguments, so an omitted argument stays omitted.
  • tests/harness/assert.js: for all seven entry points, check that a failure with a message
    carries that message, and that a failure without one fails the same way. The existing checks only
    asserted that something was thrown, which a TypeError satisfies.
  • .github/workflows/test.yml: add 26.x.

The matrix entry is in the same commit on purpose. 26.x is the only version where the new check has
any teeth, so without it the test is inert. Happy to split it out if you would rather keep matrix
changes separate, and note that it puts the count at five Node.js versions while
#37 is still open about dropping 20.x. For what
it is worth, 20.x and 25.x are both past end of life now (last releases v20.20.2 and v25.9.0, both
March 2026) while 26.x is Current and was untested.

Verification

Test suite plus lint, all green:

  • Windows, MSVC: v24.14.0, v26.5.1
  • Linux, Docker node:<major>-bookworm, GCC 12.2: 20 (through ts-strip.js, as CI does), 22, 25, 26

Teeth check on the new test, running tests/harness/assert.js directly rather than through the
runner: with the fix reverted it fails on v26.5.1 with

assert.strictEqual must fail the same way with and without a message, but without one it failed
with "TypeError: The "message" argument must be one of type string or function. Received undefined"
instead of AssertionError

and passes on v24.18.1, which is exactly why the matrix entry is needed.

Claude Opus 5 found this, wrote the fix and the test, and drafted this text; I reviewed both.

implementors/node/assert.js wraps each assertion in an arrow function with named
parameters and forwards them positionally, so a call that leaves the message out
passes an explicit undefined instead.

Node.js 26 reads the message as a variadic tuple (nodejs/node#58849, first
released in v26.0.0), where [undefined] is a message of the wrong type rather
than an absent one. assert.strictEqual(1, 2) on v26 fails with

  TypeError [ERR_INVALID_ARG_TYPE]: The "message" argument must be one of type
  string or function. Received undefined

instead of an AssertionError carrying the actual and expected values.
strictEqual, notStrictEqual and deepStrictEqual take that path; ok, match and
throws still report normally. Verified broken on v26.0.0 and v26.5.1, fine on
v25.9.0 and earlier.

It only shows up once an assertion fails, which is the moment a conformance run
is worth reading, so a runtime with a real Node-API gap gets a message about the
CTS harness rather than about its own behavior.

Forwarding with rest arguments leaves an omitted argument omitted. The harness
test now checks that every method fails the same way with and without a message,
and the CI matrix gains 26.x, the only entry that exercises it.

Signed-off-by: hexbinoct <abubakarm@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Need Triage

Development

Successfully merging this pull request may close these issues.

1 participant