facebook/react · error · Error
417
417
Error message
React currently only supports piping to one writable stream.
What it means
Thrown by the react-flight-server-fb (Flight / Server Components) node runtime: the object returned by its render-to-pipeable-stream API keeps a hasStartedFlowing closure flag, and a second pipe(destination) call throws because one Flight request can stream its output to only one writable. The request's flow state and buffered chunks are single-use, so re-piping is not a supported operation.
Source
Thrown at packages/react-flight-server-fb/src/server/ReactFlightDOMServerNode.js:223
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
- Call pipe() exactly once per request; render a new request with renderToPipeableStream(...) for each destination.
- If several sinks need the same payload, pipe once into a Node stream.PassThrough and tee the bytes from there.
- After a destination error, create a fresh render (abort the old request, re-render) instead of re-piping the same object.
Example fix
// before
const {pipe} = renderToPipeableStream(<App />, options);
pipe(res);
pipe(tee); // throws: one writable only
// after
const {pipe} = renderToPipeableStream(<App />, options);
const passThrough = new stream.PassThrough();
pipe(passThrough);
passThrough.pipe(res);
passThrough.pipe(tee); Defensive patterns
Strategy: validation
Validate before calling
let piped = false;
function pipeOnce(request, destination) {
if (piped) {
throw new Error('This stream was already piped; render a new request.');
}
piped = true;
return request.pipe(destination);
} Try / catch
try {
request.pipe(destination);
} catch (e) {
if (/one writable stream/.test(e.message)) {
const fresh = renderToPipeableStream(<App />, options);
fresh.pipe(destination);
} else {
throw e;
}
} Prevention
- Treat the pipeable-stream object as single-use: exactly one pipe() per request.
- Tee at the stream level (PassThrough) when several sinks need the same output.
- Never re-pipe after a destination error; abort and re-render instead.
When it happens
Trigger: Calling pipe(destination) twice on the same request object returned by the FB flight server's renderToPipeableStream-style API - for example piping to an HTTP response and then to a second sink (log file, cache), or re-piping to a fresh destination after the first one errored.
Common situations: Writing one render's payload to multiple sinks; retry logic that calls pipe() again after a socket error; middleware (compression, logging) that transparently pipes the same stream a second time.
Related errors
- Invalid reference.
- Values cannot be passed to next() of AsyncIterables passed t
- getProfilingData not supported by this renderer
- 417
- 248
AI-assisted analysis of facebook/react@eafeac097b (2026-08-21).
Data as JSON: /api/errors/09921abdca5153c6.
Report an issue: GitHub.