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
- Fix the underlying hydration mismatch inside the Activity boundary so the client retry never has to run.
- Ensure children of a mismatched Activity boundary can render without suspending on the client (preload lazy components / warm caches before hydrate).
- 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
- Preload/warm lazy components and suspense caches before hydrateRoot for <Activity> subtrees
- Validate server markup matches client tree inside Activity boundaries
- Pin a known-good canary version once Activity hydration works for you
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
- 558
- There should always be an Offscreen Fiber child in a hydrate
- A dehydrated Suspense node should not have a content Fiber.
- Encountered a dehydrated Suspense boundary that was previous
- 318
AI-assisted analysis of facebook/react@eafeac097b (2026-08-21).
Data as JSON: /api/errors/a9254a3d076ccd49.
Report an issue: GitHub.