facebook/react · error · Error

558

558

Error message

Client rendering an Activity suspended it again. This is a bug in React.

What it means

Thrown while completing an Activity component during hydration. If hydrating an <Activity> boundary fails, React retries it with client rendering (retryActivityComponentWithoutHydrating); that retry is expected to render synchronously without a suspense handler. If the second, hydration-free pass suspends again (DidCapture flag set), this invariant fires — React would loop forever otherwise.

Source

Thrown at packages/react-reconciler/src/ReactFiberCompleteWork.js:1538

          if (workInProgress.flags & ForceClientRender) {
            popSuspenseHandler(workInProgress);
            // Special case. There were remaining unhydrated nodes. We treat
            // this as a mismatch. Revert to client rendering.
            return workInProgress;
          } else {
            popSuspenseHandler(workInProgress);
            // Did not finish hydrating, either because this is the initial
            // render or because something suspended.
            return null;
          }
        }

        if ((workInProgress.flags & DidCapture) !== NoFlags) {
          // We called retryActivityComponentWithoutHydrating and tried client rendering
          // but now we suspended again. We should never arrive here because we should
          // not have pushed a suspense handler during that second pass and it should
          // instead have suspended above.
          throw new Error(
            'Client rendering an Activity suspended it again. This is a bug in React.',
          );
        }

        // Continue with the normal Activity path.
      }

      bubbleProperties(workInProgress);
      return null;
    }
    case SuspenseComponent: {
      const nextState: null | SuspenseState = workInProgress.memoizedState;

      // Special path for dehydrated boundaries. We may eventually move this
      // to its own fiber type so that we can add other kinds of hydration
      // boundaries that aren't associated with a Suspense tree. In anticipation
      // of such a refactor, all the hydration logic is contained in
      // this branch.

View on GitHub (pinned to eafeac097b)

Solutions

  1. Fix the underlying hydration mismatch inside the Activity boundary so the client retry never has to run.
  2. Ensure children of a mismatched Activity boundary can render without suspending on the client (preload lazy components / warm caches before hydrate).
  3. Upgrade to the latest canary React — Activity hydration is actively worked on; if it persists, file an issue with a repro.
Defensive patterns

Strategy: try-catch

Try / catch

// ErrorBoundary is the only runtime containment; fix the mismatch underneath it
class ActivityBoundary extends React.Component {
  state = {failed: false};
  static getDerivedStateFromError() { return {failed: true}; }
  render() { return this.state.failed ? this.props.fallback : this.props.children; }
}

Prevention

When it happens

Trigger: An <Activity> subtree fails hydration (mismatched markup) AND its children suspend again during the client-render retry — e.g., a lazy component or suspending data fetch inside <Activity mode="hidden"> whose server HTML doesn't match or is missing.

Common situations: Apps using the new <Activity> component together with streaming SSR, lazy() boundaries, or suspense-backed data caches, where hydration mismatches and suspending children coincide. Mostly seen on experimental/canary React builds.

Related errors


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