emberjs/ember.js · error · Error
Attempted to cast to a browser node in a non-browser context
Error message
Attempted to cast to a browser node in a non-browser context
What it means
castToBrowser is a debug utility that casts a value to a browser DOM node, checking it against the global `document`. In non-browser environments (SSR, Node tests, workers) `document` is undefined, so the cast is impossible and this error is thrown.
Source
Thrown at packages/@glimmer/debug-util/lib/simple-cast.ts:83
): NodeForSugaryCheck<S>;
export function castToBrowser<K extends keyof HTMLElementTagNameMap>(
element: SimpleElement | Element,
check: K
): HTMLElementTagNameMap[K];
export function castToBrowser<S extends SugaryNodeCheck>(
node: SimpleNode | BrowserNode | null | undefined,
sugaryCheck?: S
): 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)}`);View on GitHub (pinned to 26f97246a8)
Solutions
- Only call castToBrowser in browser contexts; guard with `typeof document !== 'undefined'`
- For SSR, use environment-agnostic code paths that skip DOM casting
- Configure tests to run in a DOM environment (jsdom/happy-dom)
- Polyfill a document if the environment is supposed to be browser-like
Example fix
// before
let node = castToBrowser(maybeNode, 'Element');
// after
if (typeof document !== 'undefined') {
let node = castToBrowser(maybeNode, 'Element');
} Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof document === 'undefined') {
throw new Error('castToBrowser requires a browser environment (document is undefined)');
} Type guard
function inBrowser(): boolean {
return typeof document !== 'undefined' && document.nodeType === 9;
} Try / catch
try {
node = castToBrowser(value, 'Element');
} catch (e) {
if (String(e.message).includes('non-browser context')) {
node = null; // SSR path
} else { throw e; }
} Prevention
- Branch on `typeof document` before any DOM-touching utility
- Run SSR code paths in CI to catch DOM assumptions
- Configure tests with jsdom/happy-dom when DOM APIs are needed
When it happens
Trigger: Calling castToBrowser (directly or via input/option/marker helpers) when `typeof document === 'undefined'` — server-side rendering, Node-only unit tests, web workers.
Common situations: Running Glimmer SSR where code paths touch DOM-casting helpers; running tests in Node without jsdom; accidentally importing DOM utilities into server bundles.
Related errors
- Attempted to cast to a browser node with a node that was not
- @glimmer/runtime needs a valid "globalThis.URL"
- you must pass document or appendOperations to a new runtime
- deprecation override for ${id} not found
- Assertion Failed: ${desc}
AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01).
Data as JSON: /api/errors/f983be06e7ce7869.
Report an issue: GitHub.