facebook/react · error · Error
318
318
Error message
A dehydrated suspense component was completed without a hydrated node. This is probably a bug in React.
What it means
completeDehydratedActivityBoundary() finishes hydrating an <Activity> boundary that kept its dehydrated state (nextState non-null). On the first pass (current === null), popHydrationState() must report that a hydrated node was adopted; if it reports none while the state says dehydrated, the server-rendered DOM for the boundary is missing, and React throws rather than build a corrupted tree.
Source
Thrown at packages/react-reconciler/src/ReactFiberCompleteWork.js:928
completedWork.childLanes = newChildLanes;
return didBailout;
}
function completeDehydratedActivityBoundary(
current: Fiber | null,
workInProgress: Fiber,
nextState: ActivityState | null,
): boolean {
const wasHydrated = popHydrationState(workInProgress);
if (nextState !== null) {
// We might be inside a hydration state the first time we're picking up this
// Activity boundary, and also after we've reentered it for further hydration.
if (current === null) {
if (!wasHydrated) {
throw new Error(
'A dehydrated suspense component was completed without a hydrated node. ' +
'This is probably a bug in React.',
);
}
prepareToHydrateHostActivityInstance(workInProgress);
bubbleProperties(workInProgress);
if (enableProfilerTimer) {
if ((workInProgress.mode & ProfileMode) !== NoMode) {
// $FlowFixMe[invalid-compare]
const isTimedOutSuspense = nextState !== null;
if (isTimedOutSuspense) {
// Don't count time spent in a timed out Suspense subtree as part of the base duration.
const primaryChildFragment = workInProgress.child;
if (primaryChildFragment !== null) {
// $FlowFixMe[unsafe-arithmetic] Flow doesn't support type casting in combination with the -= operator
workInProgress.treeBaseDuration -=
primaryChildFragment.treeBaseDuration as any as number;
}View on GitHub (pinned to eafeac097b)
Solutions
- Run the identical React version on server and client so both agree on what Activity emits
- Diff the DOM at hydration time against the SSR output to confirm the boundary markup still exists
- Disable DOM-rewriting scripts and browser extensions on the page and retest
- If the HTML is intact and it still throws, file an issue with the SSR fragment and the hydration call
Defensive patterns
Strategy: fallback
Validate before calling
// Detect tampered SSR markup before hydrating
const container = document.getElementById('app');
if (!container || container.childElementCount === 0) {
createRoot(container ?? document.createElement('div')).render(<App />);
} else {
hydrateRoot(container, <App />);
} Try / catch
hydrateRoot(container, <App />, {
onUncaughtError(error, info) {
if (/completed without a hydrated node/.test(String(error))) {
reportError(error, info.componentStack);
container.textContent = '';
createRoot(container).render(<App />); // last-resort client re-render
}
},
}); Prevention
- Run the identical React version on server and client
- Protect streamed HTML from DOM rewriters such as extensions and tag managers
- Smoke-test hydration in CI against the real SSR output
When it happens
Trigger: hydrateRoot() over HTML whose Activity boundary markup was removed or restructured before hydration - DOM stripped by scripts or extensions, streamed HTML truncated, or a hydration mismatch inside the boundary that popped the hydration state without adopting a node; also internal hydration bugs specific to Activity.
Common situations: SSR with <Activity mode="hidden"> where the hidden HTML was never emitted (server on an older React); browser extensions or in-page scripts rewriting the DOM; edge/CDM transformers mangling streamed markup; React canary after Activity shipped.
Related errors
- 558
- 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
AI-assisted analysis of facebook/react@eafeac097b (2026-08-21).
Data as JSON: /api/errors/41832b315978ab44.
Report an issue: GitHub.