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
- Move the script to the end of <body> or add defer/type="module" so the DOM exists first
- Confirm the id matches exactly (case-sensitive) in the served HTML and the entry file
- Null-check the container and fail fast with a clear message naming the missing selector
- 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
- Load the entry script with defer, type="module", or at the end of <body>
- Assert the mount element exists on boot with a descriptive error naming the selector
- Keep mount-point ids in one shared constant used by both template and entry code
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.