facebook/react · error · Error

Expected root pseudo key to be known.

Error message

Expected root pseudo key to be known.

What it means

Root pseudo keys ('DisplayName:counter') give every root a stable identity used in selection paths. Roots are keyed by setRootPseudoKey during the initial flush, filter re-mounts, and commits; removeRootPseudoKey runs when the backend tears a root down and throws if the id was never keyed. The throw means the backend is unmounting a root whose bookkeeping entry is missing - registration and teardown got out of sync.

Source

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

  // Roots don't have a real persistent identity.
  // A root's "pseudo key" is "childDisplayName:indexWithThatName".
  // For example, "App:0" or, in case of similar roots, "Story:0", "Story:1", etc.
  // We will use this to try to disambiguate roots when restoring selection between reloads.
  const rootPseudoKeys: Map<number, string> = new Map();
  const rootDisplayNameCounter: Map<string, number> = new Map();

  function setRootPseudoKey(id: number, fiber: Fiber) {
    const name = getDisplayNameForRoot(fiber);
    const counter = rootDisplayNameCounter.get(name) || 0;
    rootDisplayNameCounter.set(name, counter + 1);
    const pseudoKey = `${name}:${counter}`;
    rootPseudoKeys.set(id, pseudoKey);
  }

  function removeRootPseudoKey(id: number) {
    const pseudoKey = rootPseudoKeys.get(id);
    if (pseudoKey === undefined) {
      throw new Error('Expected root pseudo key to be known.');
    }
    const name = pseudoKey.slice(0, pseudoKey.lastIndexOf(':'));
    const counter = rootDisplayNameCounter.get(name);
    if (counter === undefined) {
      throw new Error('Expected counter to be known.');
    }
    if (counter > 1) {
      rootDisplayNameCounter.set(name, counter - 1);
    } else {
      rootDisplayNameCounter.delete(name);
    }
    rootPseudoKeys.delete(id);
  }

  function getDisplayNameForRoot(fiber: Fiber): string {
    let preferredDisplayName = null;
    let fallbackDisplayName = null;
    let child = fiber.child;

View on GitHub (pinned to eafeac097b)

Solutions

  1. Reload the page with DevTools open to rebuild root bookkeeping from scratch.
  2. Avoid changing filters at the same moment roots are being created/unmounted.
  3. Update React DevTools - root-key bookkeeping bugs get fixed in the shared package.
  4. File a React issue if it reproduces deterministically on current versions.
Defensive patterns

Strategy: fallback

Try / catch

// when embedding devtools-shared: root key bookkeeping diverged - rebuild it
try {
  renderer.handleCommitFiberRoot(root, priorityLevel);
} catch (err) {
  if (err.message.includes('Expected root pseudo key to be known')) {
    renderer.setTraceUpdateEnabled?.(false);
    location.reload(); // only a fresh session reliably rebuilds rootPseudoKeys
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: A root is unmounted while its pseudo key was never registered: roots created before DevTools attached or during a filter re-mount window (when keys are rewritten), rapid create/unmount cycles under fast refresh, or double-processing of a root unmount.

Common situations: Toggling 'hide components' filters while roots concurrently unmount; HMR swapping root components; DevTools attached mid-session to an app already tearing roots down.

Related errors


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