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

  1. Pass a document to the runtime options: { document: window.document } (or a DOM implementation like @simple-dom for SSR)
  2. For SSR/custom rendering, pass explicit appendOperations and updateOperations implementations
  3. 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

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


AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01). Data as JSON: /api/errors/563b6ca15e55f58e. Report an issue: GitHub.