From a6592e6efe933c59963d4c0d489acfd47bbdc0a2 Mon Sep 17 00:00:00 2001 From: kovan Date: Wed, 11 Mar 2026 01:24:07 +0100 Subject: [PATCH] doc: clarify readable._read() and pipe() error behavior - Rewrite readable._read() description to resolve contradictory statements about when _read() is called relative to push(). The new text clarifies that: push() may be called multiple times until it returns false; _read() will not be called again until push() provides non-empty data; and if data is not immediately available, push() should be called asynchronously when data arrives. - Clarify pipe() error caveat to specify that when the source stream is destroyed or emits an error, the destination is not closed automatically. Recommend stream.pipeline() for automatic cleanup. Verified against source: - lib/internal/streams/readable.js: kReading flag set before _read(), cleared in push(); maybeReadMore_ loops while kReading is unset - pipe() only listens for 'error' on destination, not on source Fixes: https://github.com/nodejs/node/issues/42291 Fixes: https://github.com/nodejs/node/issues/45072 Fixes: https://github.com/nodejs/node/issues/46908 Co-Authored-By: Claude Opus 4.6 --- doc/api/stream.md | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/doc/api/stream.md b/doc/api/stream.md index 65afeaad6306e0..bba7657051a755 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -1539,10 +1539,12 @@ reader.on('end', () => { }); ``` -One important caveat is that if the `Readable` stream emits an error during -processing, the `Writable` destination _is not closed_ automatically. If an -error occurs, it will be necessary to _manually_ close each stream in order -to prevent memory leaks. +One important caveat is that if the `Readable` stream is destroyed or emits an +error during processing, the `Writable` destination _is not closed_ +automatically. It will be necessary to _manually_ close each stream in order +to prevent memory leaks. The [`stream.pipeline()`][] method should be used +instead when automatic cleanup of all streams on error or completion is +desired. The [`process.stderr`][] and [`process.stdout`][] `Writable` streams are never closed until the Node.js process exits, regardless of the specified options. @@ -4228,19 +4230,19 @@ methods only. All `Readable` stream implementations must provide an implementation of the [`readable._read()`][] method to fetch data from the underlying resource. -When [`readable._read()`][] is called, if data is available from the resource, -the implementation should begin pushing that data into the read queue using the -[`this.push(dataChunk)`][stream-push] method. `_read()` will be called again -after each call to [`this.push(dataChunk)`][stream-push] once the stream is -ready to accept more data. `_read()` may continue reading from the resource and -pushing data until `readable.push()` returns `false`. Only when `_read()` is -called again after it has stopped should it resume pushing additional data into -the queue. - -Once the [`readable._read()`][] method has been called, it will not be called -again until more data is pushed through the [`readable.push()`][stream-push] -method. Empty data such as empty buffers and strings will not cause -[`readable._read()`][] to be called. +When [`readable._read()`][] is called, the implementation should push data +into the read queue using [`this.push(dataChunk)`][stream-push]. This can be +done synchronously or asynchronously. The implementation may call +[`this.push()`][stream-push] multiple times to supply data, but should stop +when [`readable.push()`][stream-push] returns `false`, which indicates the +internal buffer has reached the `highWaterMark`. Once the implementation stops +pushing, it should only resume when [`readable._read()`][] is called again. + +If data is not immediately available from the underlying resource, the +implementation should call [`this.push()`][stream-push] later, when data +becomes available. [`readable._read()`][] will not be called again until +[`readable.push()`][stream-push] is called with non-empty data (a non-empty +{Buffer}, non-empty {string}, or any value in object mode). The `size` argument is advisory. For implementations where a "read" is a single operation that returns data can use the `size` argument to determine how