Repository navigation
[worker_threads]: Main thread receives Error object when the worker throws a primitive value #35506
Description
Activity
Interestingly, when using
data:URL, the primitive object is passed to the error handler as in 14.6.0:const { Worker } = require('worker_threads'); console.log(process.versions.node); new Worker(new URL('data:text/javascript,throw 4')).on('error', err => { console.error(err); });
$ node main.js 15.0.0-pre 4
Reacted by suin and Anthony MadhvaniLooks like a consequence of 0aa3809b6b - i don't think this should be expected behavior but @addaleax can maybe confirm?
- addedconfirmed-bugIssues and PRs for confirmed bugs.Issues and PRs for confirmed bugs.workerIssues and PRs related to the worker_threads module and Worker API.Issues and PRs related to the worker_threads module and Worker API.eventtargetIssues and PRs related to the EventTarget implementation.Issues and PRs related to the EventTarget implementation.
on Oct 8, 2020 Right – this should not be happening, it’s a bug 👍
Reacted by Shelley Vohr, suin and Conrad Magnus KirschnerOk, so basically the problem here is that, because the main script inside a Worker is started from within a
.on('message')event handler, it ends up in theEventTargeterror handling chain, which is basically emitting aprocess.on('error')event first, and if there are no handlers, wrapping the exception and treating it as an uncaught one.I basically see 2 ways to solve this:
- Delay the execution of the
LOAD_SCRIPThandler with anextTick. This would make errors thrown inside of it it, including during the main script, be emitted as regular uncaught exceptions. - Don’t use the
emitUnhandledRejectionOrErr()mechanism at all for Node.js-style event listeners. It’s somewhat unexpected that the exception handling behavior of aobject.on('foo')listener depends on whetherobjectis anEventEmitterorNodeEventTarget.
In particular, this did not just change the behavior for the main script unintentionally, but also for exceptions thrown inside any
port.on('message')listeners.@jasnell @benjamingr Thoughts?
- Delay the execution of the
ping @jasnell @benjamingr any further thoughts? I’d lean towards option 2, if nobody has a strong opinion.
I lean towards option #2 as well with no strong opinions.
Missed the original ping on this... no strong opinions on either option.
This is not coming in current master, should we go ahead and create a minor version patch for this?
@yashLadha do you mind clarifying what you mean by that?
@benjamingr I meant to say that if this issue exists in an older version of node we can backport the fix to that tree, if it feels viable.
@yashLadha was this issue actually resolved in master? I don't see any PRs or work relating to it.
@yashLadha was this issue actually resolved in master? I don't see any PRs or work relating to it.
Any update on this that you know about? I've encountered an issue when handling errors thrown within a worker thread in v20
This looks already fixed on current Node. On v24.18.0, a worker that throws a primitive delivers the value itself to the parent's error handler, not a wrapped
ERR_UNHANDLED_ERROR:throw "boom" -> string 'boom'
throw 42 -> number 42
throw 7n -> bigint 7n
throw undefined -> undefined
throw null -> null
(repro: anew Worker(code, { eval: true })for each, loggingtypeof/value insideworker.on('error').)There's no regression test locking this in, though. I'd like to add one under
test/parallel/so it can't silently break again, after which this can be closed. Happy to open the PR , sound good?This looks fixed on current Node (v24.18.0), a worker throwing a primitive now surfaces the value on the parent's
errorhandler instead of a wrappedERR_UNHANDLED_ERROR.Opened #64365 to add regression coverage so it can't quietly break again.- added a commit that references this issue
on Jul 29, 2026
What steps will reproduce the bug?
Summary: Since Node v14.7.0, when primitive values are thrown in a worker, the main thread receives it as Error objects.
I'm not sure this is actually a bug. But I couldn't find out the information about this breaking change. So I have created this issue. If this is an expected change, please close this issue.
in Node v14.6.0:
in Node v14.7.0:
Other primitives:
Details
Undefined
Number
Symbol
How often does it reproduce? Is there a required condition?
There is no condition.
What is the expected behavior?
I'm not sure which is valid behavior, but at point of view of backward compatibilities, it would be the expected behavior that the main thread receives the unhandled primitive error as the primitive value instead of the Error object like the Node v14.6.0 behavior.
What do you see instead?
The main thread gets the Error object.
Additional information