Stop dispatching parent messages to self.onmessage in node:worker_threads workers #29215
+813
−10
Claude / Claude Code Review
completed
Apr 12, 2026 in 28m 55s
Code review found 1 important issue
Found 5 candidates, confirmed 2. See review comments for details.
Details
| Severity | Count |
|---|---|
| 🔴 Important | 1 |
| 🟡 Nit | 0 |
| 🟣 Pre-existing | 1 |
| Severity | File:Line | Issue |
|---|---|---|
| 🔴 Important | src/js/node/worker_threads.ts:290-296 |
parentPort.on(fn) called twice orphans first callback, leaking listenerCount |
| 🟣 Pre-existing | src/js/node/worker_threads.ts:432-435 |
fake.addListener delivers raw MessageEvent instead of event.data |
Annotations
Check failure on line 296 in src/js/node/worker_threads.ts
claude / Claude Code Review
parentPort.on(fn) called twice orphans first callback, leaking listenerCount
When `parentPort.on('message', fn)` is called twice with the same `fn`, `injectFakeEmitter.on()` creates `callback2` and overwrites `fn[wrappedListener] = callback2`, losing the reference to `callback1`. Both callbacks are stored as separate entries in `trackedByListener` (keyed by the callback object, not by `fn`), so `listenerCount` reaches 2. When `parentPort.off('message', fn)` is called, it resolves `fn[wrappedListener] = callback2` and removes only `callback2`'s entry, decrementing `listen
Check notice on line 435 in src/js/node/worker_threads.ts
claude / Claude Code Review
fake.addListener delivers raw MessageEvent instead of event.data
fake.addListener (lines 432-435) is wired directly to parentPortAddEventListener, so listeners receive a raw MessageEvent object instead of event.data, while fake.on goes through injectFakeEmitter's functionForEventType wrapper that extracts the data first. This is a pre-existing inconsistency — before this PR, addListener was self.addEventListener.bind(self), which also delivered raw events — that the PR carries forward unchanged. The fix is to wire fake.addListener to fake.on.bind(fake) so it
Loading