googleapis / googleapis/google-cloud-node

storage 8.0.1: MaxListenersExceededWarning (11 error/close listeners on PassThrough) on every createReadStream()

Open
#9,185 3 comments 10 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.