facebook/react · error · Error

There should always be a SuspenseNode parent on a mounted in

Error message

There should always be a SuspenseNode parent on a mounted instance.

What it means

Helper invariant used by the DevTools highlighter/inspector: getNearestSuspenseNode walks up the instance chain expecting to find an ancestor carrying a SuspenseNode. Hitting the root (parent null) without one means the caller asked for the nearest Suspense boundary of an instance whose subtree was never tracked as being inside any Suspense boundary.

Source

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

        const owner = getUnfilteredOwner(fiber);
        if (owner != null) {
          if (typeof owner.tag === 'number') {
            return getDisplayNameForFiber(owner as any);
          } else {
            return owner.name || '';
          }
        }
      }
      return getDisplayNameForFiber(fiber);
    } else {
      return devtoolsInstance.data.name || '';
    }
  }

  function getNearestSuspenseNode(instance: DevToolsInstance): SuspenseNode {
    while (instance.suspenseNode === null) {
      if (instance.parent === null) {
        throw new Error(
          'There should always be a SuspenseNode parent on a mounted instance.',
        );
      }
      instance = instance.parent;
    }
    return instance.suspenseNode;
  }

  function getNearestMountedDOMNode(publicInstance: Element): null | Element {
    let domNode: null | Element = publicInstance;
    while (domNode && !publicInstanceToDevToolsInstanceMap.has(domNode)) {
      // $FlowFixMe[incompatible-type]: In practice this is either null or Element.
      domNode = domNode.parentNode;
    }
    return domNode;
  }

  function getElementIDForHostInstance(

View on GitHub (pinned to eafeac097b)

Solutions

  1. Update React DevTools / react-devtools-shared to the build matching your renderer
  2. Reload with DevTools attached to rebuild suspense tracking
  3. Report the highlight/inspect action plus tree shape (any Suspense ancestors) as a repro
  4. If you embed the backend, replace this lookup with a null-returning variant for non-Suspense paths
Defensive patterns

Strategy: fallback

Type guard

// null-safe variant for embedded backends: walk ancestors, return null instead of throwing
function findNearestSuspenseNode(instance: DevToolsInstance): SuspenseNode | null {
  let cur = instance;
  while (cur !== null) {
    if (cur.suspenseNode !== null) return cur.suspenseNode;
    cur = cur.parent;
  }
  return null; // caller skips suspense-scoped work instead of crashing
}

Try / catch

try {
  return getNearestSuspenseNode(instance);
} catch (e) {
  if (/SuspenseNode parent on a mounted instance/.test(e.message)) {
    return null; // not inside any tracked Suspense boundary - skip
  }
  throw e;
}

Prevention

When it happens

Trigger: Highlighting/measuring an element whose ancestors include no Suspense boundary (or whose boundaries were skipped as filtered/unmounted) through a code path that assumes one exists; state desync when a SuspenseNode was popped but the instance still references the lookup path.

Common situations: Element highlighting or inspection on non-Suspense subtrees after version mismatches or earlier traversal failures in react-devtools-shared; embedded DevTools (React Native DevTools) on backend/renderer skew.

Related errors


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