emberjs/ember.js · error · Error
you must pass document or appendOperations to a new runtime
Error message
you must pass document or appendOperations to a new runtime
What it means
The Glimmer runtime environment needs DOM operations: either an explicit appendOperations (and updateOperations) pair, or a document from which it can construct DOMTreeConstruction and DOMChangesImpl defaults. If neither is provided, the environment would be unusable, so in DEBUG builds the constructor throws immediately with this message.
Source
Thrown at packages/@glimmer/runtime/lib/environment.ts:126
// eslint-disable-next-line @typescript-eslint/no-explicit-any
isArgumentCaptureError: ((error: any) => boolean) | undefined;
debugRenderTree: DebugRenderTree<object> | undefined;
constructor(
options: EnvironmentOptions,
private delegate: EnvironmentDelegate
) {
this.isInteractive = delegate.isInteractive;
this.debugRenderTree = this.delegate.enableDebugTooling ? new DebugRenderTree() : undefined;
this.isArgumentCaptureError = this.delegate.enableDebugTooling ? isArgumentError : undefined;
if (options.appendOperations) {
this.appendOperations = options.appendOperations;
this.updateOperations = options.updateOperations;
} else if (options.document) {
this.appendOperations = new DOMTreeConstruction(options.document);
this.updateOperations = new DOMChangesImpl(options.document);
} else if (DEBUG) {
throw new Error('you must pass document or appendOperations to a new runtime');
}
}
getAppendOperations(): GlimmerTreeConstruction {
return this.appendOperations;
}
getDOM(): GlimmerTreeChanges {
return expect(
this.updateOperations,
'Attempted to get DOM updateOperations, but they were not provided by the environment. You may be attempting to rerender in an environment which does not support rerendering, such as SSR.'
);
}
begin() {
assert(
!this[TRANSACTION],
'A glimmer transaction was begun, but one already exists. You may have a nested transaction, possibly caused by an earlier runtime exception while rendering. Please check your console for the stack trace of any prior exceptions.'View on GitHub (pinned to 26f97246a8)
Solutions
- Pass a document to the runtime options: { document: window.document } (or a DOM implementation like @simple-dom for SSR)
- For SSR/custom rendering, pass explicit appendOperations and updateOperations implementations
- Check the runtime/environment construction call for a typo'd or undefined options object
Example fix
// before
new RuntimeEnvironment({});
// after
new RuntimeEnvironment({ document: window.document });
// or for SSR:
new RuntimeEnvironment({ appendOperations: new NodeDOMTreeConstruction(doc), updateOperations: new NodeDOMChanges(doc) }); Defensive patterns
Strategy: validation
Validate before calling
function assertRuntimeOptions(options: { document?: Document; appendOperations?: unknown }) {
if (!options?.document && !options?.appendOperations) {
throw new Error('runtime requires either `document` or `appendOperations`');
}
} Type guard
interface RuntimeOptions { document?: Document; appendOperations?: unknown; updateOperations?: unknown }
function hasDomTarget(o: RuntimeOptions): boolean {
return o.document != null || o.appendOperations != null;
} Try / catch
try {
const env = new RuntimeEnvironment(options);
} catch (e) {
if (String(e?.message).includes('document or appendOperations')) {
throw new Error('Pass { document } or custom DOM operations when creating the Glimmer runtime');
} throw e;
} Prevention
- Always pass a document when constructing the runtime in the browser
- For SSR, supply Node-compatible appendOperations/updateOperations explicitly
- Keep runtime bootstrapping in one reviewed factory function with options validation
- Type runtime options so document/appendOperations are visible required-ish fields
When it happens
Trigger: new DOMEnvironment()/runtime constructed with an options object that has neither options.document nor options.appendOperations (DEBUG builds only; production silently proceeds with undefined operations).
Common situations: Standalone @glimmer/runtime / Glimmer VM setup where the document option was forgotten; wiring a custom environment for SSR (server-side rendering) without supplying custom appendOperations; refactoring runtime boot code and dropping the document option.
Understand the failure class
Background: "Must pass :limit option" / "Missing required option" — required option errors explained — this error's family across 41 libraries.
Related errors
- Attempted to cast to a browser node in a non-browser context
- Cannot create a ${tag} inside an SVG context
- @glimmer/runtime needs a valid "globalThis.URL"
- Rehydration with nextSibling not supported
- Compile Error: ${template.problem} @ ${template.span.start}.
AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01).
Data as JSON: /api/errors/563b6ca15e55f58e.
Report an issue: GitHub.