Skip to content

stream: fix TransformStream race on cancel with pending write#62040

Open
marcopiraccini wants to merge 2 commits intonodejs:mainfrom
marcopiraccini:fix-transform-stream-race
Open

stream: fix TransformStream race on cancel with pending write#62040
marcopiraccini wants to merge 2 commits intonodejs:mainfrom
marcopiraccini:fix-transform-stream-race

Conversation

@marcopiraccini
Copy link
Contributor

Fixes: #62036

  • Fix a race condition in TransformStream where a late writer.write() racing with reader.cancel() could throw an internal TypeError: controller[kState].transformAlgorithm is not a function
  • The race occurs when cancel/abort/close clears the controller's algorithms via transformStreamDefaultControllerClearAlgorithms while a pending write reaches transformStreamDefaultControllerPerformTransform
  • Added a guard in transformStreamDefaultControllerPerformTransform to check if transformAlgorithm is still callable before invoking it

Race sequence

  1. reader.read() releases backpressure
  2. reader.cancel() triggers transformStreamDefaultSourceCancelAlgorithm, which calls transformStreamDefaultControllerClearAlgorithms β€” sets transformAlgorithm = undefined
  3. writer.write() enters transformStreamDefaultSinkWriteAlgorithm and calls transformStreamDefaultControllerPerformTransform
  4. performTransform tries to invoke transformAlgorithm() which is now undefined β†’ TypeError

Fix

Guard transformStreamDefaultControllerPerformTransform so that if transformAlgorithm has been cleared (stream is shutting down), the write returns silently rather than throwing an internal error. The writable side's existing error handling surfaces the appropriate stream-level error to the caller.

Spec note

The WHATWG streams reference implementation has the same unguarded call in TransformStreamDefaultControllerPerformTransform. The race is latent in the spec but rarely surfaces in browsers due to WebIDL callback wrapping adding an extra promise tick that changes the interleaving order (see whatwg/streams#1296). Node.js hits it consistently because it lacks that extra indirection layer.

Test plan

  • New test test/parallel/test-whatwg-transformstream-cancel-write-race.js reproduces the race.

Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
@nodejs-github-bot nodejs-github-bot added needs-ci PRs that need a full CI run. web streams labels Feb 28, 2026
@marcopiraccini marcopiraccini marked this pull request as ready for review February 28, 2026 13:15
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

lgtm

@mcollina mcollina added the request-ci Add this label to start a Jenkins CI on a PR. label Feb 28, 2026
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Feb 28, 2026
@nodejs-github-bot
Copy link
Collaborator

@codecov
Copy link

codecov bot commented Feb 28, 2026

Codecov Report

βœ… All modified and coverable lines are covered by tests.
βœ… Project coverage is 89.62%. Comparing base (35d3bc8) to head (398e700).
⚠️ Report is 17 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #62040      +/-   ##
==========================================
- Coverage   89.65%   89.62%   -0.03%     
==========================================
  Files         676      676              
  Lines      206231   206244      +13     
  Branches    39505    39502       -3     
==========================================
- Hits       184898   184851      -47     
- Misses      13463    13518      +55     
- Partials     7870     7875       +5     
Files with missing lines Coverage Ξ”
lib/internal/webstreams/transformstream.js 99.57% <100.00%> (+<0.01%) ⬆️

... and 36 files with indirect coverage changes

πŸš€ New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • πŸ“¦ JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nodejs-github-bot
Copy link
Collaborator

@mcollina mcollina added commit-queue Add this label to land a pull request using GitHub Actions. author ready PRs that have at least one approval, no pending requests for changes, and a CI started. and removed needs-ci PRs that need a full CI run. labels Mar 2, 2026
@nodejs-github-bot nodejs-github-bot added commit-queue-failed An error occurred while landing this pull request using GitHub Actions. and removed commit-queue Add this label to land a pull request using GitHub Actions. labels Mar 2, 2026
@nodejs-github-bot
Copy link
Collaborator

Commit Queue failed
- Loading data for nodejs/node/pull/62040
βœ”  Done loading data for nodejs/node/pull/62040
----------------------------------- PR info ------------------------------------
Title      stream: fix TransformStream race on cancel with pending write (#62040)
Author     Marco <marco.piraccini@gmail.com> (@marcopiraccini)
Branch     marcopiraccini:fix-transform-stream-race -> nodejs:main
Labels     author ready, web streams
Commits    2
 - stream: fix TransformStream race on cancel with pending write
 - linting fixup
Committers 1
 - marcopiraccini <marco.piraccini@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/62040
Fixes: https://github.com/nodejs/node/issues/62036
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
------------------------------ Generated metadata ------------------------------
PR-URL: https://github.com/nodejs/node/pull/62040
Fixes: https://github.com/nodejs/node/issues/62036
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
--------------------------------------------------------------------------------
   β„Ή  This PR was created on Sat, 28 Feb 2026 13:15:07 GMT
   βœ”  Approvals: 2
   βœ”  - Matteo Collina (@mcollina) (TSC): https://github.com/nodejs/node/pull/62040#pullrequestreview-3870446976
   βœ”  - Paolo Insogna (@ShogunPanda) (TSC): https://github.com/nodejs/node/pull/62040#pullrequestreview-3871584746
   βœ”  Last GitHub CI successful
   β„Ή  Last Full PR CI on 2026-02-28T21:40:44Z: https://ci.nodejs.org/job/node-test-pull-request/71504/
- Querying data for job/node-test-pull-request/71504/
βœ”  Build data downloaded
   βœ”  Last Jenkins CI successful
--------------------------------------------------------------------------------
   βœ”  No git cherry-pick in progress
   βœ”  No git am in progress
   βœ”  No git rebase in progress
--------------------------------------------------------------------------------
- Bringing origin/main up to date...
From https://github.com/nodejs/node
 * branch                  main       -> FETCH_HEAD
βœ”  origin/main is now up-to-date
- Downloading patch for 62040
From https://github.com/nodejs/node
 * branch                  refs/pull/62040/merge -> FETCH_HEAD
βœ”  Fetched commits as a6e9e3217833..398e70014ef6
--------------------------------------------------------------------------------
[main b5306f06be] stream: fix TransformStream race on cancel with pending write
 Author: marcopiraccini <marco.piraccini@gmail.com>
 Date: Sat Feb 28 14:09:57 2026 +0100
 2 files changed, 60 insertions(+), 2 deletions(-)
 create mode 100644 test/parallel/test-whatwg-transformstream-cancel-write-race.js
[main bb247edd45] linting fixup
 Author: marcopiraccini <marco.piraccini@gmail.com>
 Date: Sat Feb 28 14:23:30 2026 +0100
 1 file changed, 2 insertions(+), 1 deletion(-)
   βœ”  Patches applied
There are 2 commits in the PR. Attempting autorebase.
(node:368) [DEP0190] DeprecationWarning: Passing args to a child process with shell option true can lead to security vulnerabilities, as the arguments are not escaped, only concatenated.
(Use `node --trace-deprecation ...` to show where the warning was created)
Rebasing (2/4)
Executing: git node land --amend --yes
--------------------------------- New Message ----------------------------------
stream: fix TransformStream race on cancel with pending write

Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
PR-URL: #62040
Fixes: #62036
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>

[detached HEAD 8894af0046] stream: fix TransformStream race on cancel with pending write
Author: marcopiraccini <marco.piraccini@gmail.com>
Date: Sat Feb 28 14:09:57 2026 +0100
2 files changed, 60 insertions(+), 2 deletions(-)
create mode 100644 test/parallel/test-whatwg-transformstream-cancel-write-race.js
Rebasing (3/4)
Rebasing (4/4)
Executing: git node land --amend --yes
--------------------------------- New Message ----------------------------------
linting fixup

Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
PR-URL: #62040
Fixes: #62036
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>

[detached HEAD 10c6a05a0c] linting fixup
Author: marcopiraccini <marco.piraccini@gmail.com>
Date: Sat Feb 28 14:23:30 2026 +0100
1 file changed, 2 insertions(+), 1 deletion(-)
Successfully rebased and updated refs/heads/main.

β„Ή Add commit-queue-squash label to land the PR as one commit, or commit-queue-rebase to land as separate commits.

https://github.com/nodejs/node/actions/runs/22577662568

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author ready PRs that have at least one approval, no pending requests for changes, and a CI started. commit-queue-failed An error occurred while landing this pull request using GitHub Actions. web streams

Projects

None yet

Development

Successfully merging this pull request may close these issues.

stream/web: TransformStream race can throw internal "transformAlgorithm is not a function"

4 participants