Skip to content

fix: uncatchable error closing a half-open Duplex.toWeb() writable#64160

Closed
3zrv wants to merge 1 commit into
nodejs:mainfrom
3zrv:fix/half-open-duplex-error-catch
Closed

fix: uncatchable error closing a half-open Duplex.toWeb() writable#64160
3zrv wants to merge 1 commit into
nodejs:mainfrom
3zrv:fix/half-open-duplex-error-catch

Conversation

@3zrv

@3zrv 3zrv commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Fixes: #64120

What: Fix an uncatchable TypeError: Cannot read properties of undefined (reading 'error') thrown from internal/webstreams/adapters when closing the writable side of a half-open Duplex.toWeb() (e.g. a net socket whose peer sent FIN).

Why: The writable adapter's close() has an else-branch (taken when writableEnded is already true) that sets controller = undefined but does not detach the eos listener. When the underlying writable's finish/close fires afterward, the eos callback runs controller.error(new AbortError()) on the now-undefined controller and throws. Because the throw happens synchronously inside the finish event emit (not in the close() promise chain), user try/catch around writer.close() cannot catch it and the process crashes.

This was latent until v26.3.0. Commit cbee0de (align Readable.toWeb termination with eos) made the readable side's eos use writable: false, so reader.read() now resolves on the readable END before the writable FINISH. With allowHalfOpen: false, that lands writer.close() in the exact window where writableEnded === true but writableFinished === false, triggering the null-deref.

How: Guard both controller.error(...) calls in the eos callback with optional chaining (controller?.error(...)), completing the intent that the controller is inert once close() clears it. The eos listener is intentionally kept attached so its error-handling safety net still swallows any post-close error rather than letting that become uncaught. Adds a deterministic, network-free regression test (a Duplex with allowHalfOpen: false and a final() that defers finish to a macrotask).

@nodejs-github-bot nodejs-github-bot added needs-ci PRs that need a full CI run. web streams labels Jun 27, 2026
Signed-off-by: Mohamed Sayed <k@3zrv.com>
@3zrv 3zrv force-pushed the fix/half-open-duplex-error-catch branch from 421ee3d to aef4502 Compare June 27, 2026 02:49
@3zrv 3zrv closed this Jun 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-ci PRs that need a full CI run. web streams

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Uncatchable exception from webstream adapter

2 participants