facebook/react · error · Error

There should always be an Offscreen Fiber child in a hydrate

Error message

There should always be an Offscreen Fiber child in a hydrated Suspense boundary.

What it means

DevTools backend invariant hit while mounting the mirrored tree: on modern React (where OffscreenComponent is a known tag), a SuspenseComponent fiber that is already hydrated must have a child fiber - the Offscreen wrapper that React puts around suspense content. A null fiber.child on a hydrated boundary breaks the traversal's structural assumption about React 19+ trees.

Source

Thrown at packages/react-devtools-shared/src/backend/fiber/renderer.js:3299

      if (fiber.tag === SuspenseComponent || fiber.tag === HostRoot) {
        newSuspenseNode = createSuspenseNode(newInstance);
        // Measure this Suspense node. In general we shouldn't do this until we have
        // inserted the new children but since we know this is a FiberInstance we'll
        // just use the Fiber anyway.
        // Fallbacks get attributed to the parent so we only measure if we're
        // showing primary content.
        if (fiber.tag === SuspenseComponent) {
          if (OffscreenComponent === -1) {
            const isTimedOut = fiber.memoizedState !== null;
            if (!isTimedOut) {
              newSuspenseNode.rects = measureInstance(newInstance);
            }
          } else {
            const hydrated = isFiberHydrated(fiber);
            if (hydrated) {
              const contentFiber = fiber.child;
              if (contentFiber === null) {
                throw new Error(
                  'There should always be an Offscreen Fiber child in a hydrated Suspense boundary.',
                );
              }
            } else {
              // This Suspense Fiber is still dehydrated. It won't have any children
              // until hydration.
            }
            const isTimedOut = fiber.memoizedState !== null;
            if (!isTimedOut) {
              newSuspenseNode.rects = measureInstance(newInstance);
            }
          }
        } else {
          newSuspenseNode.rects = measureInstance(newInstance);
        }
        recordSuspenseMount(newSuspenseNode, reconcilingParentSuspenseNode);
      }
      insertChild(newInstance);

View on GitHub (pinned to eafeac097b)

Solutions

  1. Update React DevTools to the version aligned with your React release channel
  2. Let hydration finish before opening DevTools, or reload with DevTools already attached so the tree is captured post-hydration
  3. Report the boundary structure (Suspense nesting, Activity usage, hydration mode) as a repro to facebook/react
  4. If you control the build, verify only one copy of react-devtools-shared is loaded
Defensive patterns

Strategy: try-catch

Try / catch

try {
  backend.handleCommitFiberRoot(renderer, root);
} catch (e) {
  if (/Offscreen Fiber child in a hydrated Suspense boundary/.test(e.message)) {
    reportDevToolsBug(e); // hydration-shape invariant: report, then reload backend
  } else throw e;
}

Prevention

When it happens

Trigger: DevTools mirroring an SSR-hydrated Suspense boundary whose content fiber is missing; DevTools attached during hydration; a React build that omits the Offscreen wrapper (older or non-standard renderer) paired with a DevTools backend that expects it.

Common situations: React 19 / canary with an outdated DevTools extension; hydration of Suspense boundaries (including Activity/Offscreen usage) while the Components tree is open; third-party renderers reusing react-devtools-shared with different child layouts.

Related errors


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