From e47795744bc91899ee24b74ba357b1d6f51fc2c1 Mon Sep 17 00:00:00 2001 From: Renegade334 Date: Fri, 6 Jun 2025 14:43:20 +0100 Subject: [PATCH 1/2] lib: deserialize to native errors in error_serdes --- lib/internal/error_serdes.js | 25 ++++++++++------- test/parallel/test-worker-error-serdes.js | 34 +++++++++++++++++++++++ 2 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 test/parallel/test-worker-error-serdes.js diff --git a/lib/internal/error_serdes.js b/lib/internal/error_serdes.js index efe75192d9f529..8de3e3b126d4ed 100644 --- a/lib/internal/error_serdes.js +++ b/lib/internal/error_serdes.js @@ -6,7 +6,7 @@ const { EvalError, FunctionPrototypeCall, ObjectAssign, - ObjectCreate, + ObjectDefineProperties, ObjectDefineProperty, ObjectGetOwnPropertyDescriptor, ObjectGetOwnPropertyNames, @@ -15,11 +15,12 @@ const { ObjectPrototypeToString, RangeError, ReferenceError, + ReflectConstruct, + ReflectDeleteProperty, SafeSet, StringFromCharCode, StringPrototypeSubstring, SymbolFor, - SymbolToStringTag, SyntaxError, TypeError, TypedArrayPrototypeGetBuffer, @@ -28,6 +29,8 @@ const { URIError, } = primordials; +const assert = require('internal/assert'); + const { Buffer } = require('buffer'); const { inspect: { custom: customInspectSymbol } } = require('util'); @@ -40,6 +43,7 @@ const kCircularReference = 5; const kSymbolStringLength = 'Symbol('.length; +// TODO: implement specific logic for AggregateError/SuppressedError const errors = { Error, TypeError, RangeError, URIError, SyntaxError, ReferenceError, EvalError, }; @@ -166,16 +170,17 @@ function deserializeError(error) { switch (error[0]) { case kSerializedError: { const { constructor, properties } = deserialize(error.subarray(1)); - const ctor = errors[constructor]; - ObjectDefineProperty(properties, SymbolToStringTag, { - __proto__: null, - value: { __proto__: null, value: 'Error', configurable: true }, - enumerable: true, - }); + assert(errorConstructorNames.has(constructor), 'Invalid constructor'); if ('cause' in properties && 'value' in properties.cause) { properties.cause.value = deserializeError(properties.cause.value); } - return ObjectCreate(ctor.prototype, properties); + // Invoke the Error constructor to gain an object with an [[ErrorData]] internal slot + const ret = ReflectConstruct(Error, [], errors[constructor]); + // Delete any properties defined by the Error constructor before assigning from source + ArrayPrototypeForEach(ObjectGetOwnPropertyNames(ret), (key) => { + ReflectDeleteProperty(ret, key); + }); + return ObjectDefineProperties(ret, properties); } case kSerializedObject: return deserialize(error.subarray(1)); @@ -196,7 +201,7 @@ function deserializeError(error) { [customInspectSymbol]: () => '[Circular object]', }; } - require('assert').fail('This should not happen'); + assert.fail('Unknown serializer flag'); } module.exports = { serializeError, deserializeError }; diff --git a/test/parallel/test-worker-error-serdes.js b/test/parallel/test-worker-error-serdes.js new file mode 100644 index 00000000000000..8fd31c53007e14 --- /dev/null +++ b/test/parallel/test-worker-error-serdes.js @@ -0,0 +1,34 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { isNativeError } = require('util/types'); +const { Worker } = require('worker_threads'); + +const validateError = (error, ctor) => { + assert.strictEqual(error.constructor, ctor); + assert.strictEqual(Object.getPrototypeOf(error), ctor.prototype); + assert(isNativeError(error)); +}; + +{ + const w = new Worker('throw new Error()', { eval: true }); + w.on('error', common.mustCall((error) => { + validateError(error, Error); + })); +} + +{ + const w = new Worker('throw new RangeError()', { eval: true }); + w.on('error', common.mustCall((error) => { + validateError(error, RangeError); + })); +} + +{ + const w = new Worker('throw new Error(undefined, { cause: new TypeError() })', { eval: true }); + w.on('error', common.mustCall((error) => { + validateError(error, Error); + assert.notStrictEqual(error.cause, undefined); + validateError(error.cause, TypeError); + })); +} From 4a2b2781813371526cd6cfb344dcddb3d3e86a35 Mon Sep 17 00:00:00 2001 From: Renegade334 Date: Tue, 21 Apr 2026 18:57:44 +0200 Subject: [PATCH 2/2] test: unroll assertions to satisfy linter --- test/parallel/test-worker-error-serdes.js | 24 ++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/test/parallel/test-worker-error-serdes.js b/test/parallel/test-worker-error-serdes.js index 8fd31c53007e14..52c6fd3495d27c 100644 --- a/test/parallel/test-worker-error-serdes.js +++ b/test/parallel/test-worker-error-serdes.js @@ -4,31 +4,33 @@ const assert = require('assert'); const { isNativeError } = require('util/types'); const { Worker } = require('worker_threads'); -const validateError = (error, ctor) => { - assert.strictEqual(error.constructor, ctor); - assert.strictEqual(Object.getPrototypeOf(error), ctor.prototype); - assert(isNativeError(error)); -}; - { const w = new Worker('throw new Error()', { eval: true }); w.on('error', common.mustCall((error) => { - validateError(error, Error); + assert(isNativeError(error)); + assert.strictEqual(error.constructor, Error); + assert.strictEqual(Object.getPrototypeOf(error), Error.prototype); })); } { const w = new Worker('throw new RangeError()', { eval: true }); w.on('error', common.mustCall((error) => { - validateError(error, RangeError); + assert(isNativeError(error)); + assert.strictEqual(error.constructor, RangeError); + assert.strictEqual(Object.getPrototypeOf(error), RangeError.prototype); })); } { const w = new Worker('throw new Error(undefined, { cause: new TypeError() })', { eval: true }); w.on('error', common.mustCall((error) => { - validateError(error, Error); - assert.notStrictEqual(error.cause, undefined); - validateError(error.cause, TypeError); + assert(isNativeError(error)); + assert.strictEqual(error.constructor, Error); + assert.strictEqual(Object.getPrototypeOf(error), Error.prototype); + + assert(isNativeError(error.cause)); + assert.strictEqual(error.cause.constructor, TypeError); + assert.strictEqual(Object.getPrototypeOf(error.cause), TypeError.prototype); })); }