Repository navigation
stream/web internal function correct implementation leads to debuggers incorrectly detecting an unhandled rejection #51093
Description
Activity
- addedweb streamsIssues and PRs related to the Web Streams API.Issues and PRs related to the Web Streams API.debuggerIssues and PRs related to the Node.js command-line debugger.Issues and PRs related to the Node.js command-line debugger.
on Dec 8, 2023 I decided to dive in the code and found that if I replace the
PromiseRejectimplementation from the default one inprimordialsto the following hack (shown in the chromium bug tracker link in my previous post), then it works as expected. I don't know anything about the NodeJS codebase so I have no idea if using the hack has possible unintended side-effects.The hack being simply:
function PromiseReject(e) { try { return primordials.PromiseReject(e); } catch { // Empty on purpose } }
By spec,
Promise.rejectshould never, ever, ever synchronously throw under any circumstances, and that hack implies it does, so that sounds like a pretty bad scenario; https://bugs.chromium.org/p/chromium/issues/detail?id=465666&q=uncaught%20promise%20pause&can=2 is closed as wontfix. I do see that it's just about doing "catch detection", so it's not that it actually throws.Another alternative could be
async function PromiseReject(e) { throw e; }but i think that'd mess with node's callsite detection; and another might be creating the rejected promise in C++ so that the callsite info is preserved?This is an incredibly annoying error.
I'm managing to work around it by adding this condition to the uncaught exception breakpoint:!error.stack.match('node:internal/webstreams/writablestream:101') && !error.stack.match('node:internal/webstreams/adapters:110') && !error.stack.match('node:internal/webstreams/readablestream:158')
Are there any better userland workarounds?
The hack being simply:
function PromiseReject(e) { try { return primordials.PromiseReject(e); } catch { // Empty on purpose } }
By spec,
Promise.rejectshould never, ever, ever synchronously throw under any circumstances, and that hack implies it does, so that sounds like a pretty bad scenario; https://bugs.chromium.org/p/chromium/issues/detail?id=465666&q=uncaught%20promise%20pause&can=2 is closed as wontfix. I do see that it's just about doing "catch detection", so it's not that it actually throws.It looks like this hack "worked" because of this catch detection issue in particular (now fixed):
https://issues.chromium.org/issues/40283987According to @rotu comment above, the problem has been fixed in V8 version 12.3. I'm going to close this issue as the "fix" is to upgrade to Node 22 (with V8 12.4) which is scheduled to become the next LTS version this October
Misunderstood what the fix was doing, the original problem is unfortunately, not fixed. (Reopening)@r-cyr sorry I was not clear. The
catch{…}should no longer affect debugger behavior. But I don’t know if the actual issue (the debugger spuriously thinking a rejection is unhandled) is fixed.I just retried your original repro code.
It is NOT treated as an uncaught exception by the VSCode debugger (VSCode 1.91.0-insider). 🎉
It is still show a problem in Chrome (Version 128.0.6544.0 (Official Build) canary (64-bit). 😞May be fixed by #53800
May be fixed by #53800
Closing, as that PR has landed. If you disagree, feel free to reopen.
Reacted by Dan Rose and Benjamin GruenbaumI still see "uncaught" rejections from
writableStreamDefaultWriterEnsureReadyPromiseRejectedwhen closing a TCP socket with wrapped instream.Duplex.toWeb
node/lib/internal/webstreams/writablestream.js
Line 1044 in 7168295
promise: PromiseReject(error), Any reason this wasn't included in #53800?
Still happening in version 22.13.1
@avivkeller Could you please reopen? This appears to still be an issue.
Unfortunately, I'm no longer a member of the team that oversees triage in this repository, cc @nodejs/issue-triage.
github-actions commented
on May 25, 2026 on May 25, 2026 – with GitHub ActionsContributorMore actionsThis issue has been marked as stale due to 210 days of inactivity.
It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.- addedstaleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.Issues and PRs marked stale due to inactivity and scheduled for automatic closure.
on May 25, 2026 github-actions commented
on Jun 28, 2026 on Jun 28, 2026 – with GitHub ActionsContributorMore actionsThis issue has been automatically closed after 30 days of inactivity following its stale status (no activity for a total of 240 days).
If this is still relevant, feel free to reopen it or leave a comment with additional details so we can continue the discussion.

Version
v20.10.0, v22.3.0
Platform
MacOS/WSL
Subsystem
No response
What steps will reproduce the bug?
Run the following code into a debugger (Inside VSCode or in Chrome) with
Pause on uncaught exceptionschecked/enabled:How often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
When run in a debugger with
Pause on uncaught exceptionschecked, program should print "Hello world" then quit.What do you see instead?
The program will execute as expected then when the stream gets destroyed at the end, the debugger will pause after a second and show the following error as an unhandled rejection:
The program runs as expected if
Pause on uncaught exceptionsis unchecked/disabled.Additional information
The call stack shows that the error comes from https://github.com/nodejs/node/blob/main/lib/internal/webstreams/writablestream.js#L1033 but we can see that
setPromiseHandledgets called a few lines after so I don't believe that error to be correct. While looking for possible reasons, I ended up finding this which may be the root causeI also tried listening to
processevents 'uncaughtException' and 'unhandledrejection' but those were never triggered.If it's really caused by the debugger, then I don't know if it's possible to refactor the function in a way that would make debuggers happy... (and me too since we started using webstreams a lot at work... and getting that error all the time when debugging is painful)
Worth noting that the function below it, named
writableStreamDefaultWriterEnsureClosedPromiseRejectedis written similarly towritableStreamDefaultWriterEnsureReadyPromiseRejectedand could, maybe, produce the same issue. (I didn't try to trigger it)