preactjs/preact · critical · Error
Undefined parent passed to render(), this is the second argu
Error message
Undefined parent passed to render(), this is the second argument. Check if the element is available in the DOM/has the correct id.
What it means
Thrown by Preact's debug build from the options._root hook when the parentNode passed as the second argument to render() is falsy (undefined/null). The debug layer guards the mount target because rendering into nothing cannot attach a DOM tree and would silently fail. It fires before the core renderer runs (oldRoot is called after the check).
Source
Thrown at debug/src/debug.js:115
throw error;
}
}
errorInfo = errorInfo || {};
errorInfo.componentStack = getOwnerStack(vnode);
oldCatchError(error, vnode, oldVNode, errorInfo);
// when an error was handled by an ErrorBoundary we still log it, matching what
// React does in development. Errors that were not handled are rethrown by the
// core and thus surface as regular uncaught errors.
if (typeof error.then != 'function') {
console.error(error);
}
};
options._root = (vnode, parentNode) => {
if (!parentNode) {
throw new Error(
'Undefined parent passed to render(), this is the second argument.\n' +
'Check if the element is available in the DOM/has the correct id.'
);
}
let isValid;
switch (parentNode.nodeType) {
case ELEMENT_NODE:
case DOCUMENT_FRAGMENT_NODE:
case DOCUMENT_NODE:
isValid = true;
break;
default:
isValid = false;
}
if (!isValid) {
let componentName = getDisplayName(vnode);View on GitHub (pinned to e881e7e838)
Solutions
- Verify the container exists: open DevTools and run document.getElementById('app') — it must return an Element, not null.
- Move the entry script to the end of <body> or add the defer attribute so the DOM is parsed first.
- Guard the call: const root = document.getElementById('app'); if (root) render(<App/>, root);
- Check for typos in the id and that the id attribute is actually written in the served HTML, not just a template that failed to render server-side.
Example fix
// before
import { render } from 'preact';
render(<App />, document.getElementById('app'));
// after
import { render } from 'preact';
const root = document.getElementById('app');
if (!root) throw new Error('Mount node #app not found in DOM');
render(<App />, root); Defensive patterns
Strategy: validation
Validate before calling
import { ELEMENT_NODE, DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE } from './constants';
function assertMountNode(node, id) {
if (!node) {
throw new Error(`Mount node "${id}" not found. Ensure the DOM is ready and the id is correct.`);
}
const allowed = new Set([ELEMENT_NODE, DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE]);
if (!allowed.has(node.nodeType)) {
throw new Error(`Mount node "${id}" has invalid nodeType ${node.nodeType}.`);
}
return node;
}
// usage
const root = assertMountNode(document.getElementById('app'), 'app');
render(<App />, root); Type guard
// node is guaranteed by caller
function isMountableNode(node) {
return node != null && [1, 9, 11].includes(node.nodeType);
}
// runtime: if (isMountableNode(el)) render(<App/>, el); Prevention
- Place the entry <script> at the end of <body> or use the defer attribute.
- Always resolve the mount node into a variable and log it before render during onboarding.
- In tests, append a fresh container to document.body in beforeEach.
When it happens
Trigger: Calling render(<App/>, document.getElementById('does-not-exist')) when no element with that id exists; calling render() before the target node is parsed (script in <head> without defer); passing a variable that was never assigned as the second arg; SSR/Node environments where document is absent so the lookup returns undefined.
Common situations: Script tag placed in <head> before the DOM body is built; typo in the container id; bundler/DOM-timing race where the entry script runs before the mount node exists; migrating from React 17 createRoot which auto-defers vs Preact's synchronous render; Jest/RTL tests that forget to append a container to document.body.
Related errors
- Expected a valid HTML node as a second argument to render. R
- Undefined component passed to createElement() You likely fo
- Invalid type passed to createElement(): ${type} Did you acc
- Invalid type passed to createElement(): ${Array.isArray(type
- Component's "ref" property should be a function, or an objec
AI-assisted analysis of preactjs/preact@e881e7e838 (2026-08-13).
Data as JSON: /api/errors/1ed0e17dd32a36d2.
Report an issue: GitHub.