Version
26.1.0
Platform
Subsystem
stream
What steps will reproduce the bug?
import { broadcast, share } from 'node:stream/iter';
const outcome = (p) => p.then(
(value) => ({ status: 'resolved', value }),
(err) => ({ status: 'rejected', name: err.name, message: err.message }),
);
// broadcast({ signal })
{
const ac = new AbortController();
const { broadcast: bc } = broadcast({ signal: ac.signal });
const pending = outcome(bc.push()[Symbol.asyncIterator]().next());
ac.abort();
console.log('broadcast:', await pending);
}
// share(source, { signal })
{
const ac = new AbortController();
const enc = new TextEncoder();
async function* source() {
yield [enc.encode('a')];
yield [enc.encode('b')];
}
const shared = share(source(), {
highWaterMark: 1,
backpressure: 'block',
signal: ac.signal,
});
const fast = shared.pull()[Symbol.asyncIterator]();
shared.pull(); // Lagging consumer keeps the one-slot buffer full.
await fast.next();
const pending = outcome(fast.next());
ac.abort();
console.log('share:', await pending);
}
Run with
node --experimental-stream-iter repro.mjs
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
broadcast: {
status: 'rejected',
name: 'AbortError',
message: 'This operation was aborted'
}
share: {
status: 'rejected',
name: 'AbortError',
message: 'This operation was aborted'
}
Both should reject with signal.reason, which is the default AbortError from AbortController.abort().
What do you see instead?
broadcast: {
status: 'resolved',
value: [Object: null prototype] { done: true, value: undefined }
}
share: {
status: 'resolved',
value: [Object: null prototype] { done: true, value: undefined }
}
Both pending consumers resolve with { done: true, value: undefined }, treating abort as clean EOF.
Additional information
No response
Version
26.1.0
Platform
Subsystem
stream
What steps will reproduce the bug?
Run with
node --experimental-stream-iter repro.mjsHow often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
Both should reject with
signal.reason, which is the defaultAbortErrorfromAbortController.abort().What do you see instead?
Both pending consumers resolve with
{ done: true, value: undefined }, treating abort as clean EOF.Additional information
No response