Skip to content

Add a way to downgrade custom errors to native errors (limitation of HTML structured clone algorithm) #37988

Closed as not planned
@Prinzhorn

Description

@Prinzhorn

Is your feature request related to a problem? Please describe.

I'm using Worker Threads. When the thread crashes with a custom error it is not properly serialized. The following demonstrates the difference (using postMessage, the result is the same as with the error event). I'm using verror because it's very popular library, but this applies to everything that is not a native Error.

index.js

const path = require('path');
const { Worker } = require('worker_threads');

const worker = new Worker(path.join(__dirname, 'worker.js'));

worker.on('message', (msg) => {
  console.log('Message:');
  console.log(msg);
});

worker.on('error', (err) => {
  console.error('Error:');
  console.error(err);
});

worker.on('exit', (code) => {
  console.log('Exit code:');
  console.log(code);
});

worker.js

const { parentPort } = require('worker_threads');
const VError = require('verror');

parentPort.postMessage(new Error('Native Error'));
parentPort.postMessage(new VError('VError'));
npm i verror
node index.js

Message:
Error: Native Error
    at Object.<anonymous> (/home/alex/src/issues/node-worker-custom-error/worker.js:4:24)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
    at MessagePort.<anonymous> (internal/main/worker_thread.js:174:24)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:354:41)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)

Message:
{ jse_shortmsg: 'VError', jse_info: {}, message: 'VError' }

This can be very unexpected and the stack information is lost. In reality the problem I'm running into is even worse. It's not with verror but with a custom SqliteError that has it's properties as enumerable: false. This means only an empty {} will arrive in the other thread. So even if I've never thrown anything that is not an Error I still end up with not-an-error inside the error event.

Describe the solution you'd like

According to MDN the fact that Error is serializable at all is already a blessing

Native Error types can be cloned in Chrome, and Firefox is working on it.

And I assume it's not possible to serialize custom errors in general because the class might not even exist in the other thread.

What I wish for is one of these two:

  1. Node.js (V8?) walks the prototype chain and sees if it finds an Error. I'm not suggesting doing this for arbitrary objects and MDN says "The prototype chain is not walked or duplicated.". But I think subclasses of Error deserve that special treatment
  2. Something along the lines of valueOf and toJSON that can be used to aid the cloning algorithm, so downgrading the Error would be in the hands of library authors

Describe alternatives you've considered

I've tried downgrading the errors in uncaughtException event and throwing the native Error, but you cannot have an uncaught exception inside uncaughtException and have the thread crash with the new error instead.
And this solutions doesn't help others debugging the same issue and only seeing {} on their error logs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature requestIssues that request new features to be added to Node.js.staleworkerIssues and PRs related to Worker support.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions