From 84727d9065f79349ed0cb79f0a55007c7e15f40f Mon Sep 17 00:00:00 2001 From: Jeong SeokChan Date: Thu, 30 Jul 2026 00:01:34 +0900 Subject: [PATCH] stream: fix recursive WritableStream abort Recheck the stream state after signaling abort, as required by steps 3 and 4 of the WritableStreamAbort algorithm. Only reject pending close promise caches, as a cache materialized after the stream becomes errored may already contain a rejected promise. This makes the recursive abort WPT pass and removes its expected-failure entry. Refs: https://streams.spec.whatwg.org/#writable-stream-abort Signed-off-by: Jeong SeokChan --- lib/internal/webstreams/writablestream.js | 15 +++++++++------ test/wpt/status/streams.json | 8 -------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/lib/internal/webstreams/writablestream.js b/lib/internal/webstreams/writablestream.js index f0eb687be129..06441424279e 100644 --- a/lib/internal/webstreams/writablestream.js +++ b/lib/internal/webstreams/writablestream.js @@ -701,15 +701,18 @@ function writerReadyPromise(writer) { } function writableStreamAbort(stream, reason) { - const { - state, - controller, - } = stream[kState]; + const { controller } = stream[kState]; + + let state = stream[kState].state; if (state === 'closed' || state === 'errored') return PromiseResolve(); controller[kState].abortController.abort(reason); + state = stream[kState].state; + if (state === 'closed' || state === 'errored') + return PromiseResolve(); + if (stream[kState].pendingAbortRequest.abort.promise !== undefined) return stream[kState].pendingAbortRequest.abort.promise; @@ -807,7 +810,7 @@ function writableStreamRejectCloseAndClosedPromiseIfNeeded(stream) { } const closedPromiseCache = stream[kState].closedPromise; - if (closedPromiseCache !== undefined) { + if (closedPromiseCache !== undefined && isPromisePending(closedPromiseCache.promise)) { setPromiseHandled(closedPromiseCache.promise); closedPromiseCache.reject(stream[kState].storedError); } @@ -817,7 +820,7 @@ function writableStreamRejectCloseAndClosedPromiseIfNeeded(stream) { } = stream[kState]; if (writer !== undefined) { const closeCache = writer[kState].close; - if (closeCache !== undefined) { + if (closeCache !== undefined && isPromisePending(closeCache.promise)) { setPromiseHandled(closeCache.promise); closeCache.reject(stream[kState].storedError); } diff --git a/test/wpt/status/streams.json b/test/wpt/status/streams.json index 2268ffcc87ac..968b33597651 100644 --- a/test/wpt/status/streams.json +++ b/test/wpt/status/streams.json @@ -43,13 +43,5 @@ }, "transform-streams/invalid-realm.tentative.window.js": { "skip": "Browser-specific test" - }, - "writable-streams/aborting.any.js": { - "fail": { - "note": "Recursive abort() call from within an abort algorithm triggers ERR_INTERNAL_ASSERTION", - "expected": [ - "recursive abort() call from abort() aborting signal" - ] - } } }