Version
26.2.0
Platform
Subsystem
stream
What steps will reproduce the bug?
import { broadcast, push } from 'node:stream/iter';
async function check(name, pendingNext) {
const result = await Promise.race([
pendingNext.then(
(value) => ({ status: 'settled', value }),
(error) => ({ status: 'rejected', error }),
),
new Promise((resolve) => {
setTimeout(() => resolve({ status: 'timeout' }), 100);
}),
]);
if (result.status === 'timeout') {
console.log(`${name}: observed hang; expected pending next() to settle after return()`);
} else {
console.log(`${name}: settled`, result);
}
}
{
const { readable } = push();
const iter = readable[Symbol.asyncIterator]();
const pendingNext = iter.next();
await iter.return();
await check('push()', pendingNext);
}
{
const { broadcast: bc } = broadcast();
const iter = bc.push()[Symbol.asyncIterator]();
const pendingNext = iter.next();
await iter.return();
await check('broadcast()', pendingNext);
}
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
push(): settled {
status: 'settled',
value: [Object: null prototype] { value: undefined, done: true }
}
broadcast(): settled {
status: 'settled',
value: [Object: null prototype] { done: true, value: undefined }
}
pendingNext should settle after await iter.return(), normally as { value: undefined, done: true } or a rejection, so no read promise is left unresolved.
What do you see instead?
push(): observed hang; expected pending next() to settle after return()
broadcast(): observed hang; expected pending next() to settle after return()
pendingNext stays pending and the timeout wins, because next() stores a resolver for the empty read, but return() detaches/cleans up without resolving or rejecting that stored pending read.
Additional information
No response
Version
26.2.0
Platform
Subsystem
stream
What steps will reproduce the bug?
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
pendingNextshould settle after awaititer.return(), normally as{ value: undefined, done: true }or a rejection, so no read promise is left unresolved.What do you see instead?
pendingNextstays pending and the timeout wins, becausenext()stores a resolver for the empty read, butreturn()detaches/cleans up without resolving or rejecting that stored pending read.Additional information
No response