googleapis / googleapis/google-cloud-node
teeny-request throws when response handler destroys the request stream
- Dominant language
- TypeScript
- Stars
- 3.2k
- Forks
- 712
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 99
Description
### Library Name
`teeny-request` 10.1.3
### Try this API
Not applicable. This is a local Node.js stream lifecycle failure in `teeny-request`; the remote HTTP response succeeds.
### Minimal reproduction
https://gist.github.com/rockwotj/a629eea7b9ccbd7e354659b9f09fead3
Run:
```sh
npm install teeny-request@10.1.3 nock
node repro.js
```
On Node.js 24.16.0 this reports an unhandled rejection:
```text
Error [ERR_STREAM_UNABLE_TO_PIPE]: Cannot pipe to a closed or destroyed stream
```
### Steps and root cause
1. Start consuming the stream returned by `teenyRequest()`.
2. Destroy that stream from its `response` event handler. Retry wrappers do this when discarding a response before another attempt.
3. `teeny-request` then runs its later `response` listener and calls `pipeline(responseStream, requestStream)` with the destroyed destination.
4. Node rejects the pipeline setup with `ERR_STREAM_UNABLE_TO_PIPE`; the rejection is not handled, and the response body is not disposed.
The stream-mode implementation defers piping until both reading has begun and the fetch response exists, but it does not recheck whether the returned request stream was destroyed by an earlier `response` listener.
### Expected behavior
If the consumer destroys the returned stream before piping starts, `teeny-request` should skip pipeline construction and destroy the unused response body. No unhandled rejection should escape.
This is the same production stack previously reported in googleapis/teeny-request#351 and googleapis/teeny-request#352. Those reports were automatically closed for lacking a runnable reproduction.
Contributor guide
Assessment
This issue has not been assessed yet.