emberjs/ember.js · error · Error
Attempted to cast to a browser node with a node that was not
Error message
Attempted to cast to a browser node with a node that was not created from this document
What it means
castToBrowser verifies that the node's ownerDocument matches the current global document. When a node was created by a different document (e.g. an iframe, jsdom instance, or detached document), the cast is rejected to prevent cross-document DOM mixing, which causes subtle browser bugs.
Source
Thrown at packages/@glimmer/debug-util/lib/simple-cast.ts:91
): Document | NodeForSugaryCheck<S> | null {
if (!LOCAL_DEBUG) {
return node as Document | NodeForSugaryCheck<S> | null;
}
if (node === null || node === undefined) {
return null;
}
if (typeof document === 'undefined') {
throw new Error('Attempted to cast to a browser node in a non-browser context');
}
if (isDocument(node)) {
return node as Document;
}
if (node.ownerDocument !== document) {
throw new Error(
'Attempted to cast to a browser node with a node that was not created from this document'
);
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- @fixme
return checkBrowserNode(node, sugaryCheck!);
}
function checkError(from: string, check: SugaryNodeCheck): Error {
return new Error(`cannot cast a ${from} into ${String(check)}`);
}
export const ELEMENT_NODE = 1;
export const DOCUMENT_NODE = 9;
function isDocument(node: Node | SimpleNode | SimpleDocument): node is Document | SimpleDocument {
return node.nodeType === DOCUMENT_NODE;
}View on GitHub (pinned to 26f97246a8)
Solutions
- Ensure nodes are created via the same global document the app runs in
- Use `document.importNode` / `adoptNode` to move nodes between documents before casting
- In tests, use a single shared jsdom document
- Avoid caching DOM nodes across iframe reloads
Example fix
// before
let el = iframe.contentDocument.createElement('div');
castToBrowser(el, 'Element');
// after
let el = document.importNode(iframe.contentDocument.createElement('div'), true);
castToBrowser(el, 'Element'); Defensive patterns
Strategy: validation
Validate before calling
function isFromCurrentDocument(node) {
return node != null && node.ownerDocument === document;
} Type guard
function isLocalNode(node) { return node != null && typeof node === 'object' && node.ownerDocument === document; } Try / catch
try {
node = castToBrowser(value, 'Element');
} catch (e) {
if (String(e.message).includes('not created from this document')) {
node = document.importNode(value, true);
} else { throw e; }
} Prevention
- Never create DOM nodes with foreign documents (iframes) and use them in the main document
- Use document.importNode/adoptNode for cross-document moves
- Share a single jsdom instance across tests
When it happens
Trigger: Passing a node created in an iframe or another Document instance to castToBrowser; mixing jsdom documents across test files; moving nodes between windows.
Common situations: Testing with multiple jsdom instances; code that clones or adopts nodes across iframes; browser extension content scripts operating on elements from different documents.
Related errors
- Attempted to cast to a browser node in a non-browser context
- deprecation override for ${id} not found
- Assertion Failed: ${desc}
- BUG: owner is missing renderer
- You must pass both the owner and args to super() in your com
AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01).
Data as JSON: /api/errors/4729bc154f6f1d03.
Report an issue: GitHub.