From 0afb013090a81f517e5470c05d7e150ccd000e01 Mon Sep 17 00:00:00 2001 From: Minho Yoo <13810291+irontaek@users.noreply.github.com> Date: Wed, 14 Oct 2026 00:08:10 +0900 Subject: [PATCH 1/2] benchmark: throw on invalid type in fs and webstorage The default branch of the type switch constructed an Error without throwing it, so an unknown type silently benchmarked an undefined path instead of failing. Assisted-by: a closed-source coding agent Signed-off-by: Minho Yoo <13810291+irontaek@users.noreply.github.com> --- benchmark/fs/bench-accessSync.js | 2 +- benchmark/fs/bench-chmodSync.js | 2 +- benchmark/fs/bench-chownSync.js | 2 +- benchmark/fs/bench-existsSync.js | 2 +- benchmark/fs/bench-fchmodSync.js | 2 +- benchmark/fs/bench-fsyncSync.js | 2 +- benchmark/fs/bench-linkSync.js | 2 +- benchmark/fs/bench-mkdirSync.js | 2 +- benchmark/fs/bench-mkdtempSync.js | 2 +- benchmark/fs/bench-openSync.js | 2 +- benchmark/fs/bench-opendirSync.js | 2 +- benchmark/fs/bench-readSync.js | 2 +- benchmark/fs/bench-readlinkSync.js | 2 +- benchmark/fs/bench-rmdirSync.js | 2 +- benchmark/fs/bench-symlinkSync.js | 2 +- benchmark/fs/bench-timesSync.js | 2 +- benchmark/fs/bench-unlinkSync.js | 2 +- benchmark/fs/bench_fdatasyncSync.js | 2 +- benchmark/webstorage/getItem.js | 2 +- benchmark/webstorage/removeItem.js | 2 +- benchmark/webstorage/setItem.js | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/benchmark/fs/bench-accessSync.js b/benchmark/fs/bench-accessSync.js index 0be0ca799d3..b4bc2a06a44 100644 --- a/benchmark/fs/bench-accessSync.js +++ b/benchmark/fs/bench-accessSync.js @@ -48,7 +48,7 @@ function main({ n, type, method }) { path = tmpdir.resolve(`.non-existing-file-${process.pid}`); break; default: - new Error('Invalid type'); + throw new Error('Invalid type'); } if (method === 'access') { diff --git a/benchmark/fs/bench-chmodSync.js b/benchmark/fs/bench-chmodSync.js index 87b45c06b50..97903db7b93 100644 --- a/benchmark/fs/bench-chmodSync.js +++ b/benchmark/fs/bench-chmodSync.js @@ -36,6 +36,6 @@ function main({ n, type }) { bench.end(n); break; default: - new Error('Invalid type'); + throw new Error('Invalid type'); } } diff --git a/benchmark/fs/bench-chownSync.js b/benchmark/fs/bench-chownSync.js index 18e2bd35d52..bcd83a34264 100644 --- a/benchmark/fs/bench-chownSync.js +++ b/benchmark/fs/bench-chownSync.js @@ -49,6 +49,6 @@ function main({ n, type, method }) { break; } default: - new Error('Invalid type'); + throw new Error('Invalid type'); } } diff --git a/benchmark/fs/bench-existsSync.js b/benchmark/fs/bench-existsSync.js index f9da2765b13..2b04eeb5167 100644 --- a/benchmark/fs/bench-existsSync.js +++ b/benchmark/fs/bench-existsSync.js @@ -27,7 +27,7 @@ function main({ n, type }) { path = tmpdir.resolve(`.non-existing-file-${process.pid}`); break; default: - new Error('Invalid type'); + throw new Error('Invalid type'); } bench.start(); diff --git a/benchmark/fs/bench-fchmodSync.js b/benchmark/fs/bench-fchmodSync.js index 4ada8a6f5d8..7023d0d1ff9 100644 --- a/benchmark/fs/bench-fchmodSync.js +++ b/benchmark/fs/bench-fchmodSync.js @@ -28,7 +28,7 @@ function main({ n, type }) { files = new Array(n).fill(tmpdir.resolve(`.non-existing-file-${Date.now()}`)); break; default: - new Error('Invalid type'); + throw new Error('Invalid type'); } const fds = files.map((x) => { diff --git a/benchmark/fs/bench-fsyncSync.js b/benchmark/fs/bench-fsyncSync.js index fc7ef292667..672d91e369e 100644 --- a/benchmark/fs/bench-fsyncSync.js +++ b/benchmark/fs/bench-fsyncSync.js @@ -24,7 +24,7 @@ function main({ n, type }) { fd = 1 << 30; break; default: - new Error('Invalid type'); + throw new Error('Invalid type'); } bench.start(); diff --git a/benchmark/fs/bench-linkSync.js b/benchmark/fs/bench-linkSync.js index ead39c338fe..cb004837d26 100644 --- a/benchmark/fs/bench-linkSync.js +++ b/benchmark/fs/bench-linkSync.js @@ -45,6 +45,6 @@ function main({ n, type }) { break; } default: - new Error('Invalid type'); + throw new Error('Invalid type'); } } diff --git a/benchmark/fs/bench-mkdirSync.js b/benchmark/fs/bench-mkdirSync.js index 1db1c587877..08698b427ee 100644 --- a/benchmark/fs/bench-mkdirSync.js +++ b/benchmark/fs/bench-mkdirSync.js @@ -29,7 +29,7 @@ function main({ n, type, recursive }) { files = new Array(n).fill(__dirname); break; default: - new Error('Invalid type'); + throw new Error('Invalid type'); } bench.start(); diff --git a/benchmark/fs/bench-mkdtempSync.js b/benchmark/fs/bench-mkdtempSync.js index afb342366bb..1e287493b6f 100644 --- a/benchmark/fs/bench-mkdtempSync.js +++ b/benchmark/fs/bench-mkdtempSync.js @@ -27,7 +27,7 @@ function main({ n, type }) { prefix = tmpdir.resolve('non-existent', 'foo', 'bar'); break; default: - new Error('Invalid type'); + throw new Error('Invalid type'); } bench.start(); diff --git a/benchmark/fs/bench-openSync.js b/benchmark/fs/bench-openSync.js index b17c0a1e67f..26ea1300b9b 100644 --- a/benchmark/fs/bench-openSync.js +++ b/benchmark/fs/bench-openSync.js @@ -33,7 +33,7 @@ function main({ n, type }) { path = tmpdir.resolve(`.non-existing-file-${process.pid}`); break; default: - new Error('Invalid type'); + throw new Error('Invalid type'); } runBench({ n, path }); diff --git a/benchmark/fs/bench-opendirSync.js b/benchmark/fs/bench-opendirSync.js index f24faa5d5e6..2ea68192b58 100644 --- a/benchmark/fs/bench-opendirSync.js +++ b/benchmark/fs/bench-opendirSync.js @@ -25,7 +25,7 @@ function main({ n, type }) { files = [tmpdir.resolve(`.non-existing-file-${Date.now()}`)]; break; default: - new Error('Invalid type'); + throw new Error('Invalid type'); } bench.start(); diff --git a/benchmark/fs/bench-readSync.js b/benchmark/fs/bench-readSync.js index a75d1750002..fea37f4c43c 100644 --- a/benchmark/fs/bench-readSync.js +++ b/benchmark/fs/bench-readSync.js @@ -38,7 +38,7 @@ function main({ n, type }) { fd = 1 << 30; break; default: - new Error('Invalid type'); + throw new Error('Invalid type'); } const buffer = Buffer.alloc(bufferSize); diff --git a/benchmark/fs/bench-readlinkSync.js b/benchmark/fs/bench-readlinkSync.js index 9b3bbceb42e..57a5611b9f3 100644 --- a/benchmark/fs/bench-readlinkSync.js +++ b/benchmark/fs/bench-readlinkSync.js @@ -49,6 +49,6 @@ function main({ n, type }) { break; } default: - new Error('Invalid type'); + throw new Error('Invalid type'); } } diff --git a/benchmark/fs/bench-rmdirSync.js b/benchmark/fs/bench-rmdirSync.js index 48103439274..71002d41c5a 100644 --- a/benchmark/fs/bench-rmdirSync.js +++ b/benchmark/fs/bench-rmdirSync.js @@ -37,6 +37,6 @@ function main({ n, type }) { break; } default: - new Error('Invalid type'); + throw new Error('Invalid type'); } } diff --git a/benchmark/fs/bench-symlinkSync.js b/benchmark/fs/bench-symlinkSync.js index 92ceb3579a4..c8864527463 100644 --- a/benchmark/fs/bench-symlinkSync.js +++ b/benchmark/fs/bench-symlinkSync.js @@ -46,6 +46,6 @@ function main({ n, type }) { break; } default: - new Error('Invalid type'); + throw new Error('Invalid type'); } } diff --git a/benchmark/fs/bench-timesSync.js b/benchmark/fs/bench-timesSync.js index 8ea879a63ef..09d5a3891c0 100644 --- a/benchmark/fs/bench-timesSync.js +++ b/benchmark/fs/bench-timesSync.js @@ -56,6 +56,6 @@ function main({ n, type, func }) { break; } default: - new Error('Invalid type'); + throw new Error('Invalid type'); } } diff --git a/benchmark/fs/bench-unlinkSync.js b/benchmark/fs/bench-unlinkSync.js index 8b992198c8d..9b134aa678c 100644 --- a/benchmark/fs/bench-unlinkSync.js +++ b/benchmark/fs/bench-unlinkSync.js @@ -28,7 +28,7 @@ function main({ n, type }) { files = new Array(n).fill(tmpdir.resolve(`.non-existing-file-${Date.now()}`)); break; default: - new Error('Invalid type'); + throw new Error('Invalid type'); } bench.start(); diff --git a/benchmark/fs/bench_fdatasyncSync.js b/benchmark/fs/bench_fdatasyncSync.js index 9aedb0a314f..cd6babadeee 100644 --- a/benchmark/fs/bench_fdatasyncSync.js +++ b/benchmark/fs/bench_fdatasyncSync.js @@ -24,7 +24,7 @@ function main({ n, type }) { fd = 1 << 30; break; default: - new Error('Invalid type'); + throw new Error('Invalid type'); } bench.start(); diff --git a/benchmark/webstorage/getItem.js b/benchmark/webstorage/getItem.js index f92ee0318ff..b82960a6f48 100644 --- a/benchmark/webstorage/getItem.js +++ b/benchmark/webstorage/getItem.js @@ -70,6 +70,6 @@ function main({ n, type }) { bench.end(n); break; default: - new Error('Invalid type'); + throw new Error('Invalid type'); } } diff --git a/benchmark/webstorage/removeItem.js b/benchmark/webstorage/removeItem.js index 0de686d45a3..738ae8a2426 100644 --- a/benchmark/webstorage/removeItem.js +++ b/benchmark/webstorage/removeItem.js @@ -70,6 +70,6 @@ function main({ n, type }) { bench.end(n); break; default: - new Error('Invalid type'); + throw new Error('Invalid type'); } } diff --git a/benchmark/webstorage/setItem.js b/benchmark/webstorage/setItem.js index 5d88a39ef52..fa53bab20df 100644 --- a/benchmark/webstorage/setItem.js +++ b/benchmark/webstorage/setItem.js @@ -58,6 +58,6 @@ function main({ n, type }) { bench.end(n); break; default: - new Error('Invalid type'); + throw new Error('Invalid type'); } } From 2c08205011c6899c96a584073f697a1cef66ac59 Mon Sep 17 00:00:00 2001 From: Minho Yoo <13810291+irontaek@users.noreply.github.com> Date: Wed, 14 Oct 2026 00:08:11 +0900 Subject: [PATCH 2/2] tools: disallow constructing an Error without using it Add a no-restricted-syntax selector next to the existing ThrowStatement one. It would have caught the benchmark bug fixed in the previous commit; the two intentional sites are disabled inline. Assisted-by: a closed-source coding agent Signed-off-by: Minho Yoo <13810291+irontaek@users.noreply.github.com> --- benchmark/error/error.js | 1 + test/parallel/test-errors-systemerror.js | 1 + tools/eslint/eslint.config_utils.mjs | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/benchmark/error/error.js b/benchmark/error/error.js index c856f3e07f6..b4b5292d041 100644 --- a/benchmark/error/error.js +++ b/benchmark/error/error.js @@ -9,6 +9,7 @@ const bench = common.createBenchmark(main, { function main({ n }) { bench.start(); for (let i = 0; i < n; ++i) + // eslint-disable-next-line no-restricted-syntax new Error('test'); bench.end(n); } diff --git a/test/parallel/test-errors-systemerror.js b/test/parallel/test-errors-systemerror.js index 629a3d1c086..651e7f8d896 100644 --- a/test/parallel/test-errors-systemerror.js +++ b/test/parallel/test-errors-systemerror.js @@ -7,6 +7,7 @@ const { E, SystemError, codes, kIsNodeError } = require('internal/errors'); const { inspect } = require('internal/util/inspect'); assert.throws( + // eslint-disable-next-line no-restricted-syntax () => { new SystemError(); }, { name: 'TypeError', diff --git a/tools/eslint/eslint.config_utils.mjs b/tools/eslint/eslint.config_utils.mjs index 7b09047937b..e6008c45793 100644 --- a/tools/eslint/eslint.config_utils.mjs +++ b/tools/eslint/eslint.config_utils.mjs @@ -16,6 +16,10 @@ export const noRestrictedSyntaxCommonAll = [ selector: 'ThrowStatement > CallExpression[callee.name=/Error$/]', message: 'Use `new` keyword when throwing an `Error`.', }, + { + selector: 'ExpressionStatement > NewExpression[callee.name=/Error$/]', + message: 'Throw, return, or assign the `Error`; constructing it alone does nothing.', + }, { selector: "CallExpression[callee.property.name='substr']", message: 'Use String.prototype.slice() or String.prototype.substring() instead of String.prototype.substr()',