vitest-dev/vitest · error · UserInputElementTypeError

received value must an HTMLElement or an SVGElement or a Loc

Error message

received value must an HTMLElement or an SVGElement or a Locator that returns an HTMLElement or an SVGElement.

What it means

Thrown by `getElementFromUserInput` (as `UserInputElementTypeError`, a `GenericTypeError` subclass) when the value passed to an element-based matcher is neither an `HTMLElement`, an `SVGElement`, nor a `Locator` that resolves to one. The helper first unwraps a `Locator` via `.element()`, then checks `instanceof defaultView.HTMLElement || instanceof defaultView.SVGElement`; anything else (Document, Window, string, number, null, plain object, a Node that is not an Element) is rejected. The resulting message is the matcher-specific 'received value must … or a Locator that returns …' string.

Source

Thrown at packages/browser/src/client/tester/expect/utils.ts:55

  elementOrLocator: Element | Locator | null,
  // TODO: minifier doesn't keep names, so we need to update this
  matcherFn: (...args: any) => any,
  context: MatcherState,
): HTMLElement | SVGElement {
  if (elementOrLocator instanceof Locator) {
    elementOrLocator = elementOrLocator.element()
  }

  const defaultView = elementOrLocator?.ownerDocument?.defaultView || window

  if (
    elementOrLocator instanceof defaultView.HTMLElement
    || elementOrLocator instanceof defaultView.SVGElement
  ) {
    return elementOrLocator
  }

  throw new UserInputElementTypeError(
    elementOrLocator,
    matcherFn,
    context,
  )
}

export function getNodeFromUserInput(
  elementOrLocator: Element | Locator,
  matcherFn: (...args: any) => any,
  context: MatcherState,
): Node {
  if (elementOrLocator instanceof Locator) {
    elementOrLocator = elementOrLocator.element()
  }

  const defaultView = elementOrLocator.ownerDocument?.defaultView || window

  if (

View on GitHub (pinned to 1fa9837ec2)

Solutions

  1. Use a Locator: `expect(page.getByRole('button')).toBeVisible()` instead of passing a selector string.
  2. If you have a raw DOM node, ensure it is an Element (e.g. `document.querySelector('#x')`) and not a Document/NodeList.
  3. For elements from iframes, pass the iframe's content element directly or build a Locator scoped to the iframe.

Example fix

// before
expect('#submit').toBeVisible()

// after
expect(page.getByRole('button', { name: 'Submit' })).toBeVisible()
Defensive patterns

Strategy: type-guard

Validate before calling

function isElementOrLocator(v: unknown): v is Element | Locator {
  return v instanceof Element || (!!v && typeof v === 'object' && Symbol.for('$$vitest:locator') in v)
}
if (!isElementOrLocator(target)) throw new TypeError('pass an Element or Locator')
expect(target).toBeVisible()

Type guard

function isElementOrLocator(v: unknown): v is Element | Locator {
  return v instanceof Element
    || (!!v && typeof v === 'object' && Symbol.for('$$vitest:locator') in (v as object))
}

Prevention

When it happens

Trigger: Passing a CSS selector string instead of an element/Locator; passing `document` or `window`; passing the result of `querySelectorAll` (a NodeList); passing null/undefined; passing an element from a different document whose `defaultView` differs.

Common situations: Confusing `@testing-library`-style string selectors with Vitest browser matchers (which need a Locator); passing a detached or cross-frame element; refactor that swapped an element for its wrapper object.

Related errors


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