jestjs/jest · error · Error

JSDOM did not return a Window object

Error message

JSDOM did not return a Window object

What it means

After constructing a JSDOM instance the environment assigns `this.global = this.dom.window`. If `dom.window` is null/undefined (jsdom failed to build a Window), the environment cannot install globals and throws at index.ts:98. This guards every subsequent global assignment from a null dereference.

Source

Thrown at packages/jest-environment-jsdom-abstract/src/index.ts:98

        : '<!DOCTYPE html>',
      {
        pretendToBeVisual: true,
        resources:
          typeof projectConfig.testEnvironmentOptions.userAgent === 'string'
            ? new ResourceLoader({
                userAgent: projectConfig.testEnvironmentOptions.userAgent,
              })
            : undefined,
        runScripts: 'dangerously',
        url: 'http://localhost/',
        virtualConsole,
        ...projectConfig.testEnvironmentOptions,
      },
    );
    const global = (this.global = this.dom.window as unknown as Win);

    if (global == null) {
      throw new Error('JSDOM did not return a Window object');
    }

    // TODO: remove at some point - for "universal" code (code should use `globalThis`)
    global.global = global;

    // Node's error-message stack size is limited at 10, but it's pretty useful
    // to see more than that when a test fails.
    this.global.Error.stackTraceLimit = 100;
    installCommonGlobals(global, projectConfig.globals);

    // TODO: remove this ASAP, but it currently causes tests to run really slow
    global.Buffer = Buffer;

    // Report uncaught errors.
    this.errorEventListener = event => {
      if (userErrorListenerCount === 0 && event.error != null) {
        process.emit('uncaughtException', event.error);
      }

View on GitHub (pinned to f49721c78e)

Solutions

  1. Simplify testEnvironmentOptions to defaults and re-add options one at a time to find the trigger.
  2. Upgrade/downgrade jsdom to a known-good version matching the adapter.
  3. Run a minimal JSDOM script in isolation to confirm jsdom itself produces a window.
  4. Check for native dependency issues (canvas) and install the prebuilt binaries if needed.

Example fix

// before (jest.config)
testEnvironmentOptions: {
  html: complexBrokenHtmlString,
  resources: new (require('jsdom').ResourceLoader)({ strictSSL: 'maybe' }),
}

// after
testEnvironmentOptions: {
  html: '<!DOCTYPE html>',
}
Defensive patterns

Strategy: try-catch

Validate before calling

// sanity-check jsdom in isolation before running the suite
const {JSDOM} = require('jsdom');
const dom = new JSDOM('<!DOCTYPE html>');
if (dom.window == null) throw new Error('jsdom produced no window');

Type guard

const hasWindow = (dom: { window: unknown | null }): dom is { window: Window } => dom.window != null;

Try / catch

try {
  testEnvironmentOptions = { html };
} catch (e) {
  // fall back to defaults if a custom html/resource breaks window creation
  testEnvironmentOptions = {};
}

Prevention

When it happens

Trigger: JSDOM internally fails to produce a window — e.g. an unrecoverable parse error on the provided `testEnvironmentOptions.html`, a jsdom build that is corrupted, or a VirtualConsole/ResourceLoader misconfiguration that short-circuits window creation.

Common situations: Passing invalid `testEnvironmentOptions.html` (non-string handled, but a bad ResourceLoader/userAgent object can throw inside jsdom); an incompatible jsdom version that changed constructor semantics; a native module load failure in jsdom on a restricted CI image.

Related errors


AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03). Data as JSON: /data/errors/46eeb94265b2c432.json. Report an issue: GitHub.