Skip to content

Close a native stream at the handoff of its data - #7502

Merged
jasnell merged 2 commits into
mainfrom
jasnell/ts-streams-husk-state
Sep 25, 2026
Merged

jasnell merged 2 commits into
mainfrom
jasnell/ts-streams-husk-state

Conversation

@jasnell

@jasnell jasnell commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator

A native-backed stream whose source was extracted (a native-to-native pipe, the C++ bridge's pumpTo) or detached (new Request(request), socket upgrades), the source of a native tee, and a teed queued branch stayed readable forever with their controllers. The Node.js interop closed-promise never settled, so finished() never fired. The error hook on a detached native body reached the moved source and broke the detached stream with an internal TypeError.

These streams now close at the handoff and drop their controller, as the legacy C++ streams leave them. The closed-promise settles then, and the interop error hook, which acts only on a readable stream, can no longer reach the moved source. A queued detached stream is unchanged: it stays the controller's stream, closing and erroring with the source.

C++ has no interop hooks. Behind the experimental
typescript_implemented_streams flag.

A native-backed stream whose source was extracted (a native-to-native
pipe, the C++ bridge's pumpTo) or detached (new Request(request), socket
upgrades), the source of a native tee, and a teed queued branch stayed
readable forever with their controllers. The Node.js interop
closed-promise never settled, so finished() never fired. The error hook
on a detached native body reached the moved source and broke the
detached stream with an internal TypeError.

These streams now close at the handoff and drop their controller, as
the legacy C++ streams leave them. The closed-promise settles then, and
the interop error hook, which acts only on a readable stream, can no
longer reach the moved source. A queued detached stream is unchanged:
it stays the controller's stream, closing and erroring with the source.

C++ has no interop hooks. Behind the experimental
typescript_implemented_streams flag.
@jasnell
jasnell requested review from guybedford and npaun September 24, 2026 18:20
@jasnell
jasnell requested review from a team as code owners September 24, 2026 18:20
@ask-bonk

ask-bonk Bot commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

LGTM

github run

@jasnell
jasnell added this pull request to stack #7505 September 24, 2026 18:28

@guybedford guybedford left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Checked out and verified locally: stream-ts@, stream-ts@all-compat-flags, stream-cpp@, sockets-ts@, sockets-ts@all-compat-flags, sockets-cpp@ all pass, and with readable.ts reverted to main both new tests fail, so they pin the fix. Legacy parity checks out too (ReadableStreamInternalController::removeSource leaves Locked + disturbed + Closed; ReadableStreamJsController::tee closes the teed stream as well).

One stale comment not in the diff: the [kControllerErrorFunction] doc comment (readable.ts ~L4609-4612) still says a branch that has itself been teed "stays what tee() left it: a permanently locked, inert shell". It is now closed at tee, so the hook returns at the #state !== 'readable' check; worth updating to match AGENTS.md.

The Windows CI failure (connect-neuter-test@ / @all-compat-flags plus two timeouts) looks unrelated: that test runs with nodejs_compat_v2 only, without the TS streams flag, and depends on a 10ms wait. A rerun should clear it.

Comment thread src/per_isolate/webstreams/readable.ts
A branch that has itself been teed is now closed at the tee, so the
comments on #consumer and on the interop error hook no longer describe
it as an inert shell.

The native arm of detachReadableStream no longer calls neutralize():
closeReadableStreamHusk already drops the consumer, so the arm marks the
stream disturbed and locks it directly, as the native tee arm does. No
behavior change.
@jasnell
jasnell merged commit bf5bcb4 into main Sep 25, 2026
23 checks passed
@jasnell
jasnell deleted the jasnell/ts-streams-husk-state branch September 25, 2026 01:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants