fix(worker): carry the deserialization failure in a worker's messageerror data - #489
Conversation
…rror data
A message a worker (or its parent) cannot read fires messageerror with
data === null, while the same failure on a MessagePort delivers the
thrown exception as data (NativeMessagePort::Drain), which is also what
Node hands worker.on('messageerror'). The node:worker_threads shim
forwards event.data unchanged, so its Worker and parentPort surfaces
received null where Node gives the Error.
Worker::OnMessageCallback now mirrors the port path: the caught
exception when there is one, null otherwise.
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (1)
Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review. 📝 WalkthroughWalkthroughWhen message deserialization fails, ChangesWorker message error data
Priority: ➖ Normal Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix Suggested reviewers: Merge Risk: ⚪ Minimal · up to Worker messageerror events now carry the deserialization exception when available and use null otherwise. No material merge risk is identified. Architecture SummaryArchitecture risk: 🔵 Low · up to The change affects 1 system. Changed systems: Architecture concerns Review detailsSystems and components
Before / after behavior
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. A rabbit reads the worker’s note Comment |
A message that cannot be deserialized on the receiving side fires
messageerror. On aMessagePortthat event'sdatais the thrown exception (NativeMessagePort::Drain), which is also what Node handsport.on('messageerror')andworker.on('messageerror'). On aWorker— parent-sideworker.onmessageerrorand the worker scope'sonmessageerror—Worker::OnMessageCallbackdiscarded the exception and deliverednull.The
node:worker_threadsshim forwardsevent.dataas the listener argument, soworker.on('messageerror', err)andparentPort.on('messageerror', err)receivednullwhere Node gives theError, while a plainMessageChannelport on the same runtime gave theError.Worker::OnMessageCallbacknow mirrors the port path: the caught exception when there is one,nullotherwise (a thrownundefinedincluded, since #477 made delivery store the payload as given).Why no new spec
Every receiver-side failure is guarded on the sender or only reachable during teardown: ports are validated against the transfer list before posting, host objects degrade or reject at write time, the
consumed_double-read guard needs a fan-out that cannot carry transferables, and the DOMException rebuild only fails once the builtin can no longer load. The one organic trigger would be V8's recursive deserializer hitting its stack check on a thread with less stack than the sender's, and probing that showed a worker thread overflows its real stack (SIGBUS) before V8's limit fires — a separate problem, reported separately. Exercising this path deterministically needs a corrupt-stream test hook, which is more product surface than this fix warrants.Full suite on
fix/messageerror-data: 1741 specs, 0 failures (iOS 18.5 simulator).Follow-up to #477.
Summary by CodeRabbit
messageerrorevent data. If no exception is available, the event data isnull.