Skip to content

stream/web internal function correct implementation leads to debuggers incorrectly detecting an unhandled rejection #51093

Closed
@r-cyr

Description

@r-cyr

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 exceptions checked/enabled:

import { ReadableStream, WritableStream } from "stream/web";

await new ReadableStream({
  async start(controller) {
    controller.enqueue('Hello world');
    controller.close();
  },
}).pipeTo(
  new WritableStream({
    write(elem) {
      console.log(elem);
    },
  })
);

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 exceptions checked, 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:

TypeError [ERR_INVALID_STATE]: Invalid state: Writer has been released

The program runs as expected if Pause on uncaught exceptions is 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 setPromiseHandled gets 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 cause

I also tried listening to process events '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 writableStreamDefaultWriterEnsureClosedPromiseRejected is written similarly to writableStreamDefaultWriterEnsureReadyPromiseRejected and could, maybe, produce the same issue. (I didn't try to trigger it)

Metadata

Metadata

Assignees

No one assigned

    Labels

    debuggerIssues and PRs related to the debugger subsystem.web streams

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions