facebook/react · error · Error

Expected to find root ID.

Error message

Expected to find root ID.

What it means

The React 15 backend decorates renderer.Reconciler.mountComponent to observe mounts. In the finally block, when parentIDStack empties (the top-level operation finished) it looks up the root id recorded for that instance in internalInstanceToRootIDMap - a WeakMap filled only when the decorated mountComponent observes the top-level wrapper. The throw means a mount completed at the top level for an instance whose root was never recorded: the tree started mounting before DevTools attached, or SSR markup bypassed the decorated path (the _topLevelWrapper SSR early-return at renderer.js:230-234).

Source

Thrown at packages/react-devtools-shared/src/backend/legacy/renderer.js:263

        // Remember the root.
        internalInstanceToRootIDMap.set(
          internalInstance,
          getID(hostContainerInfo._topLevelWrapper),
        );

        try {
          // $FlowFixMe[object-this-reference] found when upgrading Flow
          const result = fn.apply(this, args);
          parentIDStack.pop();
          return result;
        } catch (err) {
          parentIDStack = [];
          throw err;
        } finally {
          if (parentIDStack.length === 0) {
            const rootID = internalInstanceToRootIDMap.get(internalInstance);
            if (rootID === undefined) {
              throw new Error('Expected to find root ID.');
            }
            flushPendingEvents(rootID);
          }
        }
      },
      performUpdateIfNecessary(fn, args) {
        const internalInstance = args[0];
        if (getElementType(internalInstance) === ElementTypeOtherOrUnknown) {
          // $FlowFixMe[object-this-reference] found when upgrading Flow
          return fn.apply(this, args);
        }

        const id = getID(internalInstance);
        parentIDStack.push(id);

        const prevChildren = getChildren(internalInstance);
        try {
          // $FlowFixMe[object-this-reference] found when upgrading Flow

View on GitHub (pinned to eafeac097b)

Solutions

  1. Load the DevTools hook before the app's React initializes (open DevTools first, or include the react-devtools hook script as the first tag) so every mount is observed.
  2. Reload the page with DevTools already open - a fresh session records all roots.
  3. Disable duplicate React extensions that may race attachment.
  4. If embedding, wrap attach() and call the legacy backend's cleanup() when this error appears so the app is un-patched.
Defensive patterns

Strategy: fallback

Try / catch

// when embedding: if the legacy backend diverges, un-patch and let the app continue
try {
  legacyRenderer.attach(hook, rendererID, renderer, global);
} catch (err) {
  if (err.message.includes('Expected to find root ID')) {
  cleanupLegacyBackend(); // restore original Reconciler methods
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Attaching DevTools after the app already rendered, then any subsequent mount that finishes a top-level operation for pre-attach instances; or server-rendered React 15 markup whose client-side reconciliation reaches the finally block without a recorded root.

Common situations: Injecting DevTools late (bookmarklet, delayed extension load, dynamic import of react-devtools after render) into a React 15 app; hydrated React 15 pages; apps where the first user interaction triggers the first observed top-level mount.

Related errors


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