facebook / react · Issue No. 37596
createFromNodeStream does not settle a pending model when its Node Readable is destroyed without an error. A consumer awaiting that model can wait indefinitely after the input has already closed.
Current react/react main: 019019be403c3269e15b8d7ebefb57d30f84086b (React source version 19.3.0). Verified on source, not inferred from an older published release.
import {PassThrough} from 'node:stream';
import {createFromNodeStream} from 'react-server-dom-webpack/client.node';
const input = new PassThrough();
const model = createFromNodeStream(input, {
moduleMap: {}, moduleLoading: null, serverModuleMap: null,
});
let settled = false;
model.then(() => { settled = true; }, () => { settled = true; });
input.on('close', () => console.log({settled}));
input.destroy(); // Prints {settled: false} on the baseline.
A runnable repository regression test is included in the linked fix PR.
The input emits close, but the returned model remains pending. The same issue affects an interrupted development debug channel.
Reject pending work after premature readable termination, while preserving successful EOF and the original error when one is supplied.
startReadingFromStream subscribes only to data, error and end. Node destroy() can emit close without end or error. The same adapter logic exists in webpack, turbopack, parcel, ESM and the internal unbundled adapter.
Source: packages/react-server-dom-webpack/src/client/ReactFlightDOMClientNode.js.
Severity assessment: High. createFromNodeStream does not settle a pending model when its Node Readable is destroyed without an error. A consumer awaiting that model can wait indefinitely after the input has already closed.
Before: the new premature-close regression fails in all five adapters (5 failures). After: ReactFlightDOMNodeStreamLifecycle passes 30/30 in development; ReactFlightDOMNode passes 57/57 in production. The broader development run passes 54 tests and has 3 existing Windows stack-path assertion failures; an unmodified baseline run reproduces the identical 3 failures (24 passed, 3 failed). No tests were skipped or weakened to hide these failures. ESLint, Prettier and the dom-node-webpack Flow configuration pass.
Searched all Issue/PR states for createFromNodeStream, destroy, premature stream close and Connection closed. Reviewed the existing debug-channel completion work (#34236, #34304) and current adapter history. #37106 concerns exceptions in the separate server-side decodeReplyFromAsyncIterable path, not Node client readable termination. No equivalent fix was found; Discussions are disabled for this repository.
Normal end and supplied errors retain their behavior. Premature close now rejects with Node ERR_STREAM_PREMATURE_CLOSE. readable:true/writable:false avoids waiting for the writable half of a duplex debug channel. This is a termination/correctness report, not a security finding.
Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.