Skip to content

Commit 3dc56c0

Browse files
committed
stream: do not error on Duplex.from early return
`0bf9e9f833` (#65963) introduced a regression. It destroys the duplex when an async function body resolves without consuming its input, so that `pipeline()` propagates destruction upstream. It destroys it through `destroyer()`, which synthesizes an `AbortError` for any stream that is not yet finished. The original behavior, as of node 26.9.0: ``` node -v v26.9.0 node -e "require('node:stream').Duplex.from(async () => {})" // no output; this is expected ``` The bug, as of node 26.10.0: ``` node -v v26.10.0 node -e "require('node:stream').Duplex.from(async () => {})" node:events:505 throw er; // Unhandled 'error' event ^ AbortError: The operation was aborted at destroyer (node:internal/streams/destroy:328:11) at node:internal/streams/duplexify:120:13 Emitted 'error' event on Duplexify instance at: at emitErrorNT (node:internal/streams/destroy:170:8) at emitErrorCloseNT (node:internal/streams/destroy:129:3) at process.processTicksAndRejections (node:internal/process/task_queues:90:21) { code: 'ABORT_ERR' } Node.js v26.10.0 ``` This PR fixes that issue and adds a new regression test. Refs: #65963 Signed-off-by: Eric Newport <kethinov@gmail.com>
1 parent 03fcd8b commit 3dc56c0

2 files changed

Lines changed: 18 additions & 1 deletion

File tree

‎lib/internal/streams/duplexify.js‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,13 @@ module.exports = function duplexify(body, name) {
116116
// The async function returned without (fully) consuming the input.
117117
// Destroy the duplex so that pipeline propagates destruction
118118
// upstream. See https://github.com/nodejs/node/issues/55077.
119+
// Destroy it directly rather than through destroyer(), which
120+
// synthesizes an AbortError for any stream that is not finished.
121+
// The function resolved successfully, so there is no error to
122+
// report, and a duplex outside of a pipeline has nothing listening
123+
// for one.
119124
if (!finalized) {
120-
destroyer(d);
125+
d.destroy();
121126
}
122127
},
123128
(err) => {

‎test/parallel/test-stream-duplex-from.js‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,3 +434,15 @@ function makeATestWritableStream(writeFunc) {
434434
}),
435435
);
436436
}
437+
438+
// Regression for the fix to https://github.com/nodejs/node/issues/55077:
439+
// an AsyncFunction that returns without consuming its input is destroyed, but
440+
// that destruction must not be reported as an error. Outside of a pipeline
441+
// there is nothing listening for one, so an error here is unhandled and takes
442+
// the process down.
443+
{
444+
const duplex = Duplex.from(async function() {
445+
// Intentionally do not consume the async iterable input.
446+
});
447+
duplex.on('error', common.mustNotCall());
448+
}

0 commit comments

Comments
 (0)