From 6c6a23851cddf113b77de470dd6dab4538d2adb7 Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Wed, 13 May 2026 16:23:14 -0700 Subject: [PATCH 1/2] stream: uncork fromWritable writev on chunk error Ensure fromWritable().writev() uncorks the wrapped Writable when converting a later chunk throws. This prevents an internal cork from leaking after ERR_INVALID_ARG_TYPE. Fixes: https://github.com/nodejs/node/issues/63294 Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: openai:gpt-5.5 --- lib/internal/streams/iter/classic.js | 24 ++++++++++++++----- .../test-stream-iter-writable-interop.js | 16 +++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/internal/streams/iter/classic.js b/lib/internal/streams/iter/classic.js index 18d1733d6ad648..a60b7cd417c5db 100644 --- a/lib/internal/streams/iter/classic.js +++ b/lib/internal/streams/iter/classic.js @@ -608,14 +608,26 @@ function fromWritable(writable, options = kNullPrototype) { return PromiseResolve(); } - if (typeof writable.cork === 'function') writable.cork(); let ok = true; - for (let i = 0; i < chunks.length; i++) { - const bytes = toUint8Array(chunks[i]); - totalBytes += TypedArrayPrototypeGetByteLength(bytes); - ok = writable.write(bytes); + if (typeof writable.cork === 'function' && + typeof writable.uncork === 'function') { + writable.cork(); + try { + for (let i = 0; i < chunks.length; i++) { + const bytes = toUint8Array(chunks[i]); + totalBytes += TypedArrayPrototypeGetByteLength(bytes); + ok = writable.write(bytes); + } + } finally { + writable.uncork(); + } + } else { + for (let i = 0; i < chunks.length; i++) { + const bytes = toUint8Array(chunks[i]); + totalBytes += TypedArrayPrototypeGetByteLength(bytes); + ok = writable.write(bytes); + } } - if (typeof writable.uncork === 'function') writable.uncork(); if (ok) return PromiseResolve(); diff --git a/test/parallel/test-stream-iter-writable-interop.js b/test/parallel/test-stream-iter-writable-interop.js index 8a2ead0d0ee579..e7b83ac22841ce 100644 --- a/test/parallel/test-stream-iter-writable-interop.js +++ b/test/parallel/test-stream-iter-writable-interop.js @@ -550,6 +550,21 @@ function testWritevInvalidChunksType() { ); } +// ============================================================================= +// writev() uncorks when chunk validation throws +// ============================================================================= + +function testWritevInvalidChunkUncorks() { + const writable = new Writable({ write(chunk, enc, cb) { cb(); } }); + const writer = fromWritable(writable); + + assert.throws( + () => writer.writev([new Uint8Array([1]), 42]), + { code: 'ERR_INVALID_ARG_TYPE' }, + ); + assert.strictEqual(writable.writableCorked, 0); +} + // ============================================================================= // Cached writer: second call returns same instance // ============================================================================= @@ -638,6 +653,7 @@ testDrainableNull(); testDropOldestThrows(); testInvalidBackpressureThrows(); testWritevInvalidChunksType(); +testWritevInvalidChunkUncorks(); testCachedWriter(); testObjectModeThrows(); From 33ae1248bd523c74a116039992f0d95b8460257b Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Wed, 13 May 2026 16:36:02 -0700 Subject: [PATCH 2/2] stream: deduplicate classic writable writev loop --- lib/internal/streams/iter/classic.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/internal/streams/iter/classic.js b/lib/internal/streams/iter/classic.js index a60b7cd417c5db..6f978102d2b645 100644 --- a/lib/internal/streams/iter/classic.js +++ b/lib/internal/streams/iter/classic.js @@ -513,6 +513,16 @@ function fromWritable(writable, options = kNullPrototype) { return (writable.writableLength ?? 0) >= hwm; } + function writeChunks(chunks) { + let ok = true; + for (let i = 0; i < chunks.length; i++) { + const bytes = toUint8Array(chunks[i]); + totalBytes += TypedArrayPrototypeGetByteLength(bytes); + ok = writable.write(bytes); + } + return ok; + } + const writer = { __proto__: null, @@ -613,20 +623,12 @@ function fromWritable(writable, options = kNullPrototype) { typeof writable.uncork === 'function') { writable.cork(); try { - for (let i = 0; i < chunks.length; i++) { - const bytes = toUint8Array(chunks[i]); - totalBytes += TypedArrayPrototypeGetByteLength(bytes); - ok = writable.write(bytes); - } + ok = writeChunks(chunks); } finally { writable.uncork(); } } else { - for (let i = 0; i < chunks.length; i++) { - const bytes = toUint8Array(chunks[i]); - totalBytes += TypedArrayPrototypeGetByteLength(bytes); - ok = writable.write(bytes); - } + ok = writeChunks(chunks); } if (ok) return PromiseResolve();