Conversation
EventEmitterAsyncResource.prototype.emit() dropped the boolean returned by EventEmitter#emit, always yielding undefined. Code using the documented 'if (!ee.emit(...))' idiom would treat every event as unhandled even when listeners ran. runInAsyncScope already forwards the callback's return value; the return keyword was missing.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
Walkthrough
ChangesEmit Return Value Fix
Related Issues: None referenced. Related PRs: None referenced. Suggested labels: node:events, node:async_hooks Suggested reviewers: None identified. 🐰 A boolean once lost in the async fray, 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
|
Updated 3:20 PM PT - Jul 7th, 2026
❌ @robobun, your commit 243992e has 1 failures in
🧪 To try this PR locally: bunx bun-pr 33688That installs a local version of the PR into your bun-33688 --bun |
|
This PR may be a duplicate of:
🤖 Generated with Claude Code |
|
Overlap noted:
This PR is intentionally the minimal, single-line change for the |
There was a problem hiding this comment.
LGTM — one-keyword fix with a matching test; the inline note is a pre-existing adjacent issue, not something this PR introduces.
Extended reasoning...
Overview
This PR adds a single return keyword to EventEmitterAsyncResource.prototype.emit in src/js/node/events.ts so that it forwards the boolean result of super.emit(...) through runInAsyncScope instead of returning undefined. A new test in test/js/node/async_hooks/EventEmitterAsyncResource.test.ts asserts true with a listener, false without, and true for a handled error event. I confirmed AsyncResource#runInAsyncScope in src/js/node/async_hooks.ts already returns fn.$apply(thisArg, args), so the forwarded value is exactly EventEmitter#emit's boolean.
Security risks
None. This is a pure return-value propagation change in a Node compat shim; no input parsing, auth, crypto, filesystem, or network paths are touched.
Level of scrutiny
Low. The diff is a single token in a well-understood method, aligning behavior with both Node's documented contract and Bun's own base EventEmitter#emit. There are no design decisions or cross-cutting changes here.
Other factors
The bug-hunter surfaced a pre-existing issue: when captureRejections: true (or the global default) is set, the base EventEmitter constructor assigns this.emit = emitWithRejectionCapture as an own property, shadowing this prototype method entirely. That's real but orthogonal — it predates this PR, is unaffected by it, and emitWithRejectionCapture already returns a boolean, so this PR's contract fix is still correct for the paths that reach it. Fixing the shadowing would be a larger structural change to how kCapture is dispatched and belongs in its own PR. No prior human reviews or outstanding comments to address.
There was a problem hiding this comment.
LGTM — one-keyword fix that makes EventEmitterAsyncResource#emit return the boolean from super.emit, matching Node.
Extended reasoning...
Overview
Single-keyword change in src/js/node/events.ts: adds return before this.asyncResource.runInAsyncScope(() => super.emit(...args)) in EventEmitterAsyncResource.prototype.emit. runInAsyncScope already forwards the callback's return value, so this correctly propagates the boolean from EventEmitter#emit (true if listeners existed, false otherwise) instead of returning undefined. A new test in test/js/node/async_hooks/EventEmitterAsyncResource.test.ts covers the with-listener, without-listener, and error-with-listener cases.
Security risks
None. Pure return-value plumbing in a Node compat class; no auth, crypto, I/O, or untrusted-input handling touched.
Level of scrutiny
Low. This is a mechanical, one-token fix to match documented Node.js behavior, with a targeted regression test. The pre-existing captureRejections shadowing issue I noted earlier is orthogonal, acknowledged by the author as out of scope, and that thread is resolved — it doesn't affect the correctness of this change (in that configuration emitWithRejectionCapture already returns a boolean anyway).
Other factors
Two overlapping PRs (#33689, #31825) contain the same fix as part of larger changes; the author has already surfaced this in the thread and offered to defer. That's a merge-sequencing decision for a maintainer, not a code-correctness concern — the change here is correct and self-contained either way. No CODEOWNERS entry covers this path.
|
CI status: the diff itself is green. Both Buildkite runs (#69984, #70000) failed only on the which is the CI runner failing to fetch the built binary before any test executes. No test touching |
|
This landed on main through #31825, which reworked Checked by running this PR's version of that test file against a debug build of current main (05dd45e): 2 runs, 3 pass / 0 fail each time, and the Nothing left for this PR to add, so closing it. |
What does this PR do?
EventEmitterAsyncResource.prototype.emit()always returnedundefinedinstead of the boolean thatEventEmitter#emitis documented to return (trueif the event had listeners,falseotherwise). This breaks the common Node idiomif (!this.emit('error', e)) throw e;, which would treat every event as unhandled even when a listener ran.Repro
Cause
emit()calledthis.asyncResource.runInAsyncScope(() => super.emit(...args))withoutreturn.runInAsyncScopealready forwards the callback's return value; thereturnkeyword was missing.Fix
Added
returnin front ofrunInAsyncScope.How did you verify your code works?
Added a test to
test/js/node/async_hooks/EventEmitterAsyncResource.test.tsassertingemit()returnstruewhen listeners exist (includingerror) andfalsewhen none do. Fails on released bun, passes with this change.