facebook/react · error · Error

299

299

Error message

Target container is not a DOM element.

What it means

createRoot checks its first argument with isValidContainer (Element, Document, or DocumentFragment nodeType) before creating a FiberRoot, and throws error 299 otherwise. The overwhelmingly common input that fails is null, returned by document.getElementById when the element is missing.

Source

Thrown at packages/react-dom/src/client/ReactDOMRoot.js:176

          console.error(
            'Attempted to synchronously unmount a root while React was already ' +
              'rendering. React cannot finish unmounting the root until the ' +
              'current render has completed, which may lead to a race condition.',
          );
        }
      }
      updateContainerSync(null, root, null, null);
      flushSyncWork();
      unmarkContainerAsRoot(container);
    }
  };

export function createRoot(
  container: Element | Document | DocumentFragment,
  options?: CreateRootOptions,
): RootType {
  if (!isValidContainer(container)) {
    throw new Error('Target container is not a DOM element.');
  }

  warnIfReactDOMContainerInDEV(container);

  const concurrentUpdatesByDefaultOverride = false;
  let isStrictMode = false;
  let identifierPrefix = '';
  let onUncaughtError = defaultOnUncaughtError;
  let onCaughtError = defaultOnCaughtError;
  let onRecoverableError = defaultOnRecoverableError;
  let onDefaultTransitionIndicator = defaultOnDefaultTransitionIndicator;
  let transitionCallbacks = null;

  // $FlowFixMe[invalid-compare]
  if (options !== null && options !== undefined) {
    if (__DEV__) {
      if ((options as any).hydrate) {
        console.warn(

View on GitHub (pinned to eafeac097b)

Solutions

  1. Move the script to the end of <body> or add defer/type="module" so the DOM exists first
  2. Confirm the id matches exactly (case-sensitive) in the served HTML and the entry file
  3. Null-check the container and fail fast with a clear message naming the missing selector
  4. If mounting must happen before parse, wrap in DOMContentLoaded

Example fix

// before (in <head>, element not parsed yet)
const root = createRoot(document.getElementById('root'));

// after (deferred entry)
const el = document.getElementById('root');
if (!el) throw new Error('#root not found — check the HTML template');
const root = createRoot(el);
Defensive patterns

Strategy: type-guard

Validate before calling

function isValidReactContainer(node: unknown): boolean {
  return !!(
    node &&
    typeof node === 'object' &&
    (node.nodeType === 1 || node.nodeType === 9 || node.nodeType === 11)
  );
}

const el = document.getElementById('root');
if (!isValidReactContainer(el)) {
  throw new Error('#root not found in the document — check the HTML template and script loading order');
}
const root = createRoot(el);

Type guard

function isValidReactContainer(node: unknown): node is Element | Document | DocumentFragment {
  return !!(
    node &&
    typeof node === 'object' &&
    (node.nodeType === 1 || node.nodeType === 9 || node.nodeType === 11)
  );
}

Prevention

When it happens

Trigger: createRoot(document.getElementById('root')) when the script runs before the DOM is parsed, the id is misspelled or case-mismatched, or the HTML template omits the root element; also passing a component instance or props object by mistake.

Common situations: Script loaded synchronously in <head> without defer; static HTML missing <div id="root">; ids renamed in the template but not in the entry file; SSR shells that do not emit the mount point.

Related errors


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