You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Assertions yield incorrect message when source is transpiled by loader #59612
To generate the assertion message, Node.js captures a stack trace in its interal getErrMessage(). That stack trace references the transpiled location though; in this case, line 0, column 74.
It then loads the active file from disk (so test.ts). For line 0 it has a special handling in getCode(), where it will use findColumn() directly. Note that the column is the absolute column value, so a line break is simply considered a character in this case.
The parser then uses acorn internally to parse the TypeScript source. This search will most of the time yield partially or completely wrong results.
tsx (through esbuild) does include a source map in the transpiled code. I'm not sure how Node.js could handle this best. Ideally it'd scan the transpiled code to find the assertion source and then reference the original file through the source map.
But looking at the current code which always loads the executed file from disk, I'm not sure if Node.js even has access to the executed code at runtime.
I'm not sure I understand what's the issue you are reporting. If I run your example as a javascript file I get:
marcoippolito@Mac test % node --test test.js
✖ line issue (5.622834ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 45.457291
✖ failing tests:
test at test.js:4:1
✖ line issue (5.622834ms)
AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
assert.ok(false)
at TestContext.<anonymous> (file:///Users/marcoippolito/Documents/projects/test/test.js:5:12)
at Test.runInAsyncScope (node:async_hooks:214:14)
at Test.run (node:internal/test_runner/test:1081:25)
at Test.start (node:internal/test_runner/test:978:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:332:17) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '==',
diff: 'simple'
}
If I run it as a .ts:
marcoippolito@Mac test % node --test test.ts
✖ line issue (5.624459ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 79.625875
✖ failing tests:
test at test.ts:4:1
✖ line issue (5.624459ms)
AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
assert.ok(false)
at TestContext.<anonymous> (file:///Users/marcoippolito/Documents/projects/test/test.ts:5:12)
at Test.runInAsyncScope (node:async_hooks:214:14)
at Test.run (node:internal/test_runner/test:1081:25)
at Test.start (node:internal/test_runner/test:978:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:332:17) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '==',
diff: 'simple'
}
They are identical, so not sure how this is related to type stripping.
The stacktrace is correct and so is the line throwing the error. TSX can include more lines into the stacktrace that's up to the library
And yes, as I pointed out, when running the TypeScript file through Node directly, it produces the correct result. The issue is with loaders, so when you run it with tsx --test test.ts.
As I mentioned in my description, I did open an issue with tsx, but after investigating the issue further, I saw the issue on the Node.js side, not solvable by loaders like tsx.
This is due to the fact that Node.js uses a stack trace from the transpiled code to get the location of the assertion, but then uses that location to scan the original source code from disk for the assertion code, instead of using the transpiled code.
Running node --test test.mjs will correctly identify the assertion.
Running node --loader ./loader.mjs --test test.mjs on the other hand will fail to identify the assertion, as it is not taking the transpiled code into account.
The ErrorCaptureStackTrace primordial seems to ignore any source map (according to the debugger), even with --enable-source-maps. This results in the wrong location and thus the wrong assertion lookup.
The reproduction in #59612 (comment) is not a correct example. Manipulating the source code without source maps is deemed to mess up with the mapped stack trace.
I can add a source map into the example, though ErrorCaputreStackTrace in that position does not respect the source map for some reason. I'll look into updating the example later tonight.
Activity
marco-ippolito commentedon Aug 25, 2025
I'm not sure I understand what's the issue you are reporting. If I run your example as a javascript file I get:
If I run it as a
.ts:They are identical, so not sure how this is related to type stripping.
The stacktrace is correct and so is the line throwing the error. TSX can include more lines into the stacktrace that's up to the library
DASPRiD commentedon Aug 25, 2025
When referring to
tsx, I'm talking about the https://github.com/privatenumber/tsx, not the file extension.DASPRiD commentedon Aug 25, 2025
And yes, as I pointed out, when running the TypeScript file through Node directly, it produces the correct result. The issue is with loaders, so when you run it with
tsx --test test.ts.marco-ippolito commentedon Aug 25, 2025
Well then you should open an issue to the tsx project repo, not node
DASPRiD commentedon Aug 25, 2025
As I mentioned in my description, I did open an issue with
tsx, but after investigating the issue further, I saw the issue on the Node.js side, not solvable by loaders liketsx.This is due to the fact that Node.js uses a stack trace from the transpiled code to get the location of the assertion, but then uses that location to scan the original source code from disk for the assertion code, instead of using the transpiled code.
DASPRiD commentedon Aug 25, 2025
Here's a minimal reproduction case:
loader.mts:test.mts:Running
node --test test.mjswill correctly identify the assertion.Running
node --loader ./loader.mjs --test test.mjson the other hand will fail to identify the assertion, as it is not taking the transpiled code into account.DASPRiD commentedon Aug 26, 2025
I think the issue boils down to this:
node/lib/internal/assert/utils.js
Lines 165 to 174 in ab6991f
The
ErrorCaptureStackTraceprimordial seems to ignore any source map (according to the debugger), even with--enable-source-maps. This results in the wrong location and thus the wrong assertion lookup.legendecas commentedon Aug 27, 2025
The reproduction in #59612 (comment) is not a correct example. Manipulating the source code without source maps is deemed to mess up with the mapped stack trace.
DASPRiD commentedon Aug 27, 2025
I can add a source map into the example, though
ErrorCaputreStackTracein that position does not respect the source map for some reason. I'll look into updating the example later tonight.DASPRiD commentedon Aug 27, 2025
Okay, I've worked out a minimal reproduction case with source maps included:
test.mts:transformed.mjs(generated via esbuild):loader.mjs:Run via the following command:
This will yield the following failed test result:
We can see that the source map is correctly considered for the test location (line 9), but the assertion is incorrectly determined.
Running the
test.mtsthrough node without a loader (node --test test.mts) yields the correct assertion:legendecas commentedon Sep 11, 2025
Fixed in #59751.