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 in this package returns a handle with pipe() and abort(). React's Flight server writes each request to exactly one Writable; a hasStartedFlowing flag makes a second pipe() on the same handle throw instead of interleaving two output streams and corrupting the payload.

Source

Thrown at packages/react-server-dom-esm/src/server/ReactFlightDOMServerNode.js:204

    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. Render once per destination: call renderToPipeableStream again for each additional Writable
  2. Tee manually by forwarding the first destination's chunk events to the second sink
  3. Wrap pipe in a once-guard so a second call is rejected with your own clear error

Example fix

// before
const {pipe} = renderToPipeableStream(<App/>, opts);
pipe(httpResponse);
pipe(logStream); // throws: second pipe

// after — one render per destination
renderToPipeableStream(<App/>, opts).pipe(httpResponse);
renderToPipeableStream(<App/>, opts).pipe(logStream);
Defensive patterns

Strategy: validation

Validate before calling

export function pipeOnce(request) {
  let piped = false;
  return {
    pipe(destination) {
      if (piped) throw new Error('pipe() already called — render again for another destination');
      piped = true;
      return request.pipe(destination);
    },
    abort(reason) {
      request.abort(reason);
    },
  };
}

Prevention

When it happens

Trigger: Calling pipe() twice on one result of renderToPipeableStream — piping to the HTTP response and then also to a cache/log stream, retry logic that re-pipes after a failure, or both a request handler and a shutdown hook calling pipe on the same handle.

Common situations: Frameworks that want to tee the payload (response plus prefetch cache); error-recovery middleware that re-pipes; tests piping one render into several sinks.

Related errors


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