From 6f6376f960d8ab903464415cc88daabdb0fdf415 Mon Sep 17 00:00:00 2001 From: Wes Tarle Date: Tue, 18 Aug 2026 02:07:50 +0000 Subject: [PATCH] fix(teeny-request): destroy response stream if request stream is destroyed early If the consumer destroys the returned stream before piping starts, teeny-request should skip pipeline construction and destroy the unused response body. This prevents unhandled rejections (e.g. ERR_STREAM_UNABLE_TO_PIPE) when trying to pipe to a closed/destroyed stream. Fixes: 8670 --- core/packages/teeny-request/src/index.ts | 12 ++++++++++-- core/packages/teeny-request/test/index.ts | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/core/packages/teeny-request/src/index.ts b/core/packages/teeny-request/src/index.ts index 931861ad798c..313e4ab101a0 100644 --- a/core/packages/teeny-request/src/index.ts +++ b/core/packages/teeny-request/src/index.ts @@ -278,10 +278,18 @@ function teenyRequest( let responseStream: any; requestStream.once('reading', () => { if (responseStream) { - pipeline(responseStream, requestStream, () => {}); + if (!requestStream.destroyed) { + pipeline(responseStream, requestStream, () => {}); + } else if (responseStream && typeof responseStream.destroy === 'function') { + responseStream.destroy(); + } } else { requestStream.once('response', () => { - pipeline(responseStream, requestStream, () => {}); + if (!requestStream.destroyed) { + pipeline(responseStream, requestStream, () => {}); + } else if (responseStream && typeof responseStream.destroy === 'function') { + responseStream.destroy(); + } }); } }); diff --git a/core/packages/teeny-request/test/index.ts b/core/packages/teeny-request/test/index.ts index e34d697a2a58..a7e4cb3de4f1 100644 --- a/core/packages/teeny-request/test/index.ts +++ b/core/packages/teeny-request/test/index.ts @@ -298,6 +298,25 @@ describe('teeny', () => { }); }); + it('should not pipe if request stream is destroyed early', done => { + const scope = mockJson(); + + const stream = teenyRequest({uri}); + stream.on('error', done); + + stream.once('response', response => { + response.body.once('close', () => { + scope.done(); + done(); + }); + + // Destroy the stream before piping can be fully set up + stream.destroy(); + }); + + stream.resume(); + }); + it('should expose TeenyStatistics instance', () => { assert.ok(teenyRequest.stats instanceof TeenyStatistics); });