diff --git a/doc/api/errors.md b/doc/api/errors.md
index 78456d0d28e0f0..71671c33be47f0 100644
--- a/doc/api/errors.md
+++ b/doc/api/errors.md
@@ -2573,6 +2573,12 @@ disconnected socket.
A call was made and the UDP subsystem was not running.
+
+
+### `ERR_SOURCE_MAP_MISSING_SOURCE`
+
+A file imported from a source map was not found.
+
### `ERR_SQLITE_ERROR`
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index 87f7b1da094d93..a3275c4bcb50ac 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -1704,6 +1704,7 @@ E('ERR_SOCKET_CONNECTION_TIMEOUT',
E('ERR_SOCKET_DGRAM_IS_CONNECTED', 'Already connected', Error);
E('ERR_SOCKET_DGRAM_NOT_CONNECTED', 'Not connected', Error);
E('ERR_SOCKET_DGRAM_NOT_RUNNING', 'Not running', Error);
+E('ERR_SOURCE_MAP_MISSING_SOURCE', `Cannot find '%s' imported from the source map for '%s'`, Error);
E('ERR_SRI_PARSE',
'Subresource Integrity string %j had an unexpected %j at position %d',
SyntaxError);
diff --git a/lib/internal/test_runner/coverage.js b/lib/internal/test_runner/coverage.js
index 7ef57020728302..1b5ba7912dcb7d 100644
--- a/lib/internal/test_runner/coverage.js
+++ b/lib/internal/test_runner/coverage.js
@@ -30,6 +30,11 @@ const { tmpdir } = require('os');
const { join, resolve, relative, matchesGlob } = require('path');
const { fileURLToPath } = require('internal/url');
const { kMappings, SourceMap } = require('internal/source_map/source_map');
+const {
+ codes: {
+ ERR_SOURCE_MAP_MISSING_SOURCE,
+ },
+} = require('internal/errors');
const kCoverageFileRegex = /^coverage-(\d+)-(\d{13})-(\d+)\.json$/;
const kIgnoreRegex = /\/\* node:coverage ignore next (?\d+ )?\*\//;
const kLineEndingRegex = /\r?\n$/u;
@@ -390,6 +395,9 @@ class TestCoverage {
newUrl ??= startEntry?.originalSource;
const mappedLines = this.getLines(newUrl);
+ if (!mappedLines) {
+ throw new ERR_SOURCE_MAP_MISSING_SOURCE(newUrl, url);
+ }
const mappedStartOffset = this.entryToOffset(startEntry, mappedLines);
const mappedEndOffset = this.entryToOffset(endEntry, mappedLines) + 1;
for (let l = startEntry.originalLine; l <= endEntry.originalLine; l++) {
diff --git a/test/fixtures/test-runner/source-map-missing-sources/index.js b/test/fixtures/test-runner/source-map-missing-sources/index.js
new file mode 100644
index 00000000000000..f8a711af8c0098
--- /dev/null
+++ b/test/fixtures/test-runner/source-map-missing-sources/index.js
@@ -0,0 +1,2 @@
+export {};
+//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/test/fixtures/test-runner/source-map-missing-sources/index.js.map b/test/fixtures/test-runner/source-map-missing-sources/index.js.map
new file mode 100644
index 00000000000000..12cacc226ac5c1
--- /dev/null
+++ b/test/fixtures/test-runner/source-map-missing-sources/index.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.js.map","sourceRoot":"","sources":["nonexistent.js"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC"}
\ No newline at end of file
diff --git a/test/parallel/test-runner-coverage.js b/test/parallel/test-runner-coverage.js
index ba767283e672c4..77e0afadbdff95 100644
--- a/test/parallel/test-runner-coverage.js
+++ b/test/parallel/test-runner-coverage.js
@@ -6,6 +6,7 @@ const { readdirSync } = require('node:fs');
const { test } = require('node:test');
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
+const { pathToFileURL } = require('node:url');
const skipIfNoInspector = {
skip: !process.features.inspector ? 'inspector disabled' : false
};
@@ -320,6 +321,20 @@ test('coverage with source maps', skipIfNoInspector, () => {
assert.strictEqual(result.status, 1);
});
+test('coverage with source maps missing sources', skipIfNoInspector, () => {
+ const file = fixtures.path('test-runner', 'source-map-missing-sources', 'index.js');
+ const missing = fixtures.path('test-runner', 'source-map-missing-sources', 'nonexistent.js');
+ const result = spawnSync(process.execPath, [
+ '--test',
+ '--experimental-test-coverage',
+ file,
+ ]);
+
+ const error = `Cannot find '${pathToFileURL(missing)}' imported from the source map for '${pathToFileURL(file)}'`;
+ assert(result.stdout.toString().includes(error));
+ assert.strictEqual(result.status, 1);
+});
+
test('coverage with ESM hook - source irrelevant', skipIfNoInspector, () => {
let report = [
'# start of coverage report',