googleapis / googleapis/google-cloud-node
storage 8.0.1: MaxListenersExceededWarning (11 error/close listeners on PassThrough) on every createReadStream()
- Dominant language
- TypeScript
- Stars
- 3.2k
- Forks
- 712
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 99
Description
Package: `@google-cloud/storage` (handwritten/storage)
## Environment
- @google-cloud/storage 8.0.1 (teeny-request 11.0.1, retry-request 9.0.1)
- Node 26.5.1 (also on Vercel's Node runtime)
- Does **not** occur with 8.0.0 (teeny-request 9.0.0), same code — 8.0.1's change vs 8.0.0 is that dependency bump (teeny-request 11 moved to node-fetch 3).
## Steps to reproduce
```js
import { Storage } from "@google-cloud/storage";
const file = new Storage().bucket("").file("<~3 MB object>");
let n = 0;
for await (const chunk of file.createReadStream()) n += chunk.length;
```
Run with `node --trace-warnings`.
## Actual
Every streamed download emits both:
```
MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 error listeners added to [PassThrough]. MaxListeners is 10.
at pipelineImpl (node:internal/streams/pipeline:286:14)
at pipeline (node:internal/streams/pipeline:183:10)
at PassThrough. (teeny-request/build/src/index.js:194:43)
MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 close listeners added to [PassThrough]. MaxListeners is 10.
at eos (node:internal/streams/end-of-stream:302:10)
at pipe (node:internal/streams/pipeline:447:3)
at pipelineImpl (node:internal/streams/pipeline:374:25)
at PassThrough. (teeny-request/build/src/index.js:194:43)
```
The emitter is the node-fetch 3 response body `PassThrough` (teeny-request's `responseStream` in stream mode). Its `error` listeners when the warning fires are node-fetch's `FetchError` wrapper, three `onError`/`eos` pairs from node-fetch's own internal `pipeline` calls, teeny-request's `(err) => requestStream.emit('error', err)` forwarder (`index.js:201`), and the listeners `stream.pipeline(responseStream, requestStream)` adds at `index.js:194` — the count is reached by library wiring over node-fetch 3's body, not by the caller. (teeny-request's own repo is archived, so reporting here.)
## Expected
No warning for a single stream consumer, as with 8.0.0.
## Impact
Two warnings per download on any service streaming from GCS, which floods runtime-error dashboards. We have pinned to 8.0.0 meanwhile.
Contributor guide
Research direction
Reproduce the warning with the provided Node command and --trace-warnings, then inspect teeny-request/build/src/index.js around lines 194-201 and its responseStream handling. Compare the 8.0.0 and 8.0.1 dependency paths, focusing on node-fetch 3's PassThrough wiring. Done means a single createReadStream() consumer emits no MaxListenersExceededWarning and the regression is covered by a test.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- nodejs, typescript
- Domain
- backend, cloud
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100