facebook/react · error · Error
React currently only supports piping to one writable stream.
Error message
React currently only supports piping to one writable stream.
What it means
The object returned by the Node Flight server's pipeToNodeWritable-style API guards pipe() with a hasStartedFlowing flag: one Flight request writes to exactly one writable destination. Calling pipe() a second time on the same request throws, because the request's buffers, drain handlers, and completion state cannot be shared across two streams.
Source
Thrown at packages/react-server-dom-unbundled/src/server/ReactFlightDOMServerNode.js:211
options ? options.identifierPrefix : undefined,
options ? options.temporaryReferences : undefined,
options ? options.startTime : undefined,
__DEV__ && options ? options.environmentName : undefined,
__DEV__ && options ? options.filterStackFrame : undefined,
debugChannelReadable !== undefined,
);
let hasStartedFlowing = false;
startWork(request);
if (debugChannelWritable !== undefined) {
startFlowingDebug(request, debugChannelWritable);
}
if (debugChannelReadable !== undefined) {
startReadingFromDebugChannelReadable(request, debugChannelReadable);
}
return {
pipe<T: Writable>(destination: T): T {
if (hasStartedFlowing) {
throw new Error(
'React currently only supports piping to one writable stream.',
);
}
hasStartedFlowing = true;
startFlowing(request, destination);
destination.on('drain', createDrainHandler(destination, request));
destination.on(
'error',
createCancelHandler(
request,
'The destination stream errored while writing data.',
),
);
// We don't close until the debug channel closes.
if (!__DEV__ || debugChannelReadable === undefined) {
destination.on(
'close',
createCancelHandler(request, 'The destination stream closed early.'),View on GitHub (pinned to eafeac097b)
Solutions
- Render once per destination: call renderToPipeableStream again for each consumer instead of re-piping the same request
- For caching, tee the destination stream (stream.PassThrough piped onward) or serialize the result once and reuse it
- In retry logic, guard with your own hasPiped flag and create a fresh request rather than re-invoking pipe
Example fix
// before: same request piped twice
const {pipe} = renderToPipeableStream(<App />, {});
pipe(httpResponse);
pipe(cacheStream); // throws
// after: one request per destination
pipe(renderToPipeableStream(<App />, {}), httpResponse);
pipe(renderToPipeableStream(<App />, {}), cacheStream);
// or tee a single destination
const tee = new PassThrough();
tee.pipe(httpResponse); tee.pipe(cacheStream);
request.pipe(tee); Defensive patterns
Strategy: validation
Validate before calling
// Wrap pipe() so a second call is your own, clearer error
function pipeOnce(pipeable, destination) {
if (pipeable._piped) throw new Error('This Flight request was already piped; render a new request per destination.');
pipeable._piped = true;
return pipeable.pipe(destination);
} Try / catch
try { pipeable.pipe(dest); } catch (e) {
if (e.message.includes('only supports piping to one writable stream')) {
// recovery: create a fresh render for this destination, do not retry the same request
return pipeOnce(renderToPipeableStream(element, options), dest);
}
throw e;
} Prevention
- One render per destination; tee the writable if you need multiple sinks
- Guard retry/error-recovery code from re-entering pipe
- Keep the pipeable object private to a single request scope
When it happens
Trigger: Calling request.pipe(writable) twice — e.g. a retry loop that re-pipes on error, piping to both the HTTP response and a cache/tee, or two consumers of the same render result each calling pipe().
Common situations: Implementing render-to-cache by piping the same request to a cache stream and the client; error-handling that retries pipe after a backpressure or socket error; refactoring where pipe is called in two branches that both run.
Related errors
AI-assisted analysis of facebook/react@eafeac097b (2026-08-21).
Data as JSON: /api/errors/25c128a2615f5a35.
Report an issue: GitHub.