vitest-dev/vitest · error · Error

Cannot find iframe element. This is a bug in Vitest…

Error message

Cannot find iframe element. This is a bug in Vitest. Please, open a new issue with reproduction.

What it means

Thrown by `getIframeScale` when `window.frameElement` is null, i.e. the current execution context is the top-level window and not inside an `<iframe>`. The browser tester normally runs the test code inside an iframe so it can apply UI-scaling transforms; `getIframeScale` reads the iframe's computed `transform` to recover the scale factor. Finding no iframe means the tester's iframe embedding is broken — explicitly flagged as a Vitest bug.

Solutions

  1. Use the standard Vitest browser tester HTML shell that embeds the runner in an iframe.
  2. If you have customized the tester UI, restore the iframe element that hosts the test context.
  3. Otherwise treat this as a Vitest bug: open an issue with a minimal reproduction.
Defensive patterns

Strategy: validation

Validate before calling

// internal contract: this code must run inside the tester iframe
if (!window.frameElement) {
  throw new Error('getIframeScale must be called from within the tester iframe')
}

Prevention

When it happens

Trigger: The browser tester's iframe embedding is bypassed or removed; `getIframeScale` is called from the top-level page rather than the tester iframe; a custom HTML shell that does not embed the tester in an iframe.

Common situations: Custom browser tester entry point; an upstream Vitest change that breaks iframe embedding; running tester internals outside the standard iframe.

Related errors


AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11). Data as JSON: /api/errors/e09c1159165d3760. Report an issue: GitHub.

Appendix: source

Thrown at packages/browser/src/client/tester/tester-utils.ts:261

  }
  options_ = options_ || {} as T
  const currentTime = now()
  const endTime = startTime + timeout
  const remainingTime = Math.floor(endTime - currentTime)
  // keep some buffer to process the timeout, but always hand the provider a
  // positive value so it surfaces a descriptive, source-mapped locator error
  // instead of letting the task timer win the race with a generic timeout;
  // the buffer covers the provider->server->client round-trip of the rejection,
  // which can exceed 100ms on loaded CI machines running several browsers
  options_.timeout = Math.max(remainingTime - 250, 1)
  return options_
}

export function getIframeScale(): number {
  const iframe = window.frameElement

  if (!iframe) {
    throw new Error(`Cannot find iframe element. This is a bug in Vitest. Please, open a new issue with reproduction.`)
  }

  // DOMMatrix parses the computed 2D transform matrix [a, b, c, d, e, f]
  // `a` and `d` are the x and y scale factors - since we only apply uniform scaling, `a === d`
  const scale = new DOMMatrix(getComputedStyle(iframe).transform).a

  return scale
}

function escapeRegexForSelector(re: RegExp): string {
  // Unicode mode does not allow "identity character escapes", so we do not escape and
  // hope that it does not contain quotes and/or >> signs.
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Character_escape
  // TODO: rework RE usages in internal selectors away from literal representation to json, e.g. {source,flags}.
  if (re.unicode || (re as any).unicodeSets) {
    return String(re)
  }
  // Even number of backslashes followed by the quote -> insert a backslash.

View on GitHub (pinned to 1fa9837ec2)