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

renderToPipeableStream (react-server-dom-webpack/server.node) returns a {pipe, abort} object whose output may flow to exactly one writable destination. The first pipe() call sets hasStartedFlowing; any second call throws, because the Fizz server cannot fan a single render out to multiple writables.

Source

Thrown at packages/react-server-dom-webpack/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

  1. Call renderToPipeableStream once per request and pipe exactly once; enforce with a `let piped = false` guard if control flow is complex
  2. If the output is needed in two places, pass a PassThrough to pipe() and branch from it — never call pipe() again
  3. On destination failure use abort()/onError and start a fresh renderToPipeableStream instead of re-piping

Example fix

// before
const {pipe} = renderToPipeableStream(<App />, opts);
router.get('/a', (req, res) => pipe(res));
router.get('/b', (req, res) => pipe(res)); // second pipe -> throws

// after: one render and one pipe per request
router.get('/:page', (req, res) => {
  const {pipe} = renderToPipeableStream(<App />, opts);
  pipe(res);
});
Defensive patterns

Strategy: validation

Validate before calling

function pipeOnce(pipe, destination) {
  if (pipeOnce.called) {
    throw new Error('This stream was already piped — create a new renderToPipeableStream instead.');
  }
  pipeOnce.called = true;
  pipe(destination);
}

Prevention

When it happens

Trigger: Calling stream.pipe(destination) twice — for example a success path and an error/fallback path that both pipe, retry logic that re-pipes after a destination error, or one renderToPipeableStream result shared across two request handlers or sockets.

Common situations: Custom Express/Fastify SSR servers caching the render result between requests; error handlers that attempt to pipe a fallback shell after piping already began; middleware trying to tee output into a cache and the response by calling pipe twice.

Related errors


AI-assisted analysis of facebook/react@eafeac097b (2026-08-21). Data as JSON: /api/errors/801d34817e6b12f7. Report an issue: GitHub.