piscina icon indicating copy to clipboard operation
piscina copied to clipboard

All errors are upcast to Error in the main thread?

Open connorworley opened this issue 3 years ago • 4 comments

I'm not able to differentiate between specialized error types thrown in workers, as they appear to be upcast to Error in the main thread. This makes specializing error handling in the main thread difficult. Is there any way to work around this?

Code example:

// worker
export class MyError extends Error {
  ...
}

export default async function() {
  throw new MyError();
}
// main
const workerPool = new Piscina({
   ...
});

try {
  workerPool.run()
} catch(error) {
  if (error instanceof Error) {
    // this code path works
  }
  if (error instanceof MyError) {
    // this code path is never taken
  }
}

connorworley avatar Jul 22 '21 22:07 connorworley

random guess - maybe serialization for IPC gobbles the type up and captures only the properties? As a workaround for now, what about testing the name property (or some other custom property)

e.g. apollo's set of errors defines the name explicitly https://github.com/apollographql/apollo-server/blob/12cbe0067fe15796391d8ceeb046acae5776bb74/packages/apollo-server-errors/src/index.ts#L179-L185 and I think apollo server reads from .extensions.code to classify the error type

magicmark avatar Jul 23 '21 19:07 magicmark

It looks like name just becomes Error and any custom properties are stripped. It might be possible to parse the message property to determine the original error name? It would be nice to avoid this though.

connorworley avatar Jul 23 '21 19:07 connorworley

Node.js uses the structured clone algorithm for messaging across threads. Custom objects (subclasses, your own class objects, etc) are not supported fully by the structured clone algorithm. Error objects will always come out as the top level built-in Error they extend from (e.g. class MyError extends Error => Error, class MyTypeError extends TypeError => TypeError. Further, on custom class objects, only own properties on the objects are cloned, so if I have class Foo { get a() { return 1 } }; f = new Foo(); f.b = 2, cloning would yield only { b: 2 }

jasnell avatar Jul 26 '21 14:07 jasnell

How to resolve it? In my screen:

// child
const  err = new Error('message')
err.skip = true // main thread lost it
throw err

imcuttle avatar Aug 30 '21 09:08 imcuttle

Passing a flag or some way to identify the error is a way to go, but relying on the prototype is not recommended because of the reason @jasnell pointed out 🙂

metcoder95 avatar Jun 13 '23 20:06 metcoder95