vitest-dev/vitest · error · Error

Expected element or locator to be an instance of Element or…

Error message

Expected element or locator to be an instance of Element or Locator.

What it means

Thrown by `serializeElement` as its final fallthrough after the input is confirmed truthy, is not an `instanceof Element`, and is not a `Locator` (detected via the `$$vitest:locator` symbol). The function only knows how to serialize actual DOM elements and Locator objects; any other truthy value (a plain object, a number, a string, a Window, a Document) reaches this branch and is rejected.

Solutions

  1. Pass only a DOM `Element` or a real Vitest `Locator` instance.
  2. If using a custom Locator-like object, ensure it carries `Symbol.for('$$vitest:locator')` and a compatible shape — or, better, use the official Locator API.
  3. Guard the call site: `if (el instanceof Element || isLocator(el)) { ... }`.

Example fix

// before
await serializeElement(document) // Document is not an Element

// after
await serializeElement(document.documentElement)
Defensive patterns

Strategy: type-guard

Validate before calling

import { isLocator } from 'vitest/browser' // or local equivalent
function isSerializableTarget(v: unknown): v is Element | Locator {
  return v instanceof Element || (!!v && typeof v === 'object' && Symbol.for('$$vitest:locator') in (v as object))
}
if (!isSerializableTarget(target)) throw new TypeError('pass an Element or Locator')
await serializeElement(target)

Type guard

function isSerializableTarget(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 Window, Document, NodeList entry that is a text node, a plain object mimicking a Locator but missing the symbol, or any non-Element/non-Locator value to `serializeElement`.

Common situations: Forwarding a value that the caller assumed was an element/Locator; a fake/mock Locator missing the internal symbol; cross-realm objects whose `instanceof Element` check fails.

Related errors


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

Appendix: source

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

export async function serializeElement(elementOrLocator: Element | Locator, options?: SelectorOptions): Promise<SerializedLocator> {
  if (!elementOrLocator) {
    throw new Error('Expected element or locator to be defined.')
  }
  if (elementOrLocator instanceof Element) {
    const selector = convertElementToCssSelector(elementOrLocator)
    return { selector, locator: __INTERNAL._asLocator('javascript', selector) }
  }
  if (isLocator(elementOrLocator)) {
    if (provider === 'playwright' || kElementLocator in elementOrLocator) {
      return elementOrLocator.serialize()
    }
    const element = await elementOrLocator.findElement(options)
    const selector = convertElementToCssSelector(element)
    const locator = __INTERNAL._asLocator('javascript', selector)
    return { selector, locator }
  }
  throw new Error('Expected element or locator to be an instance of Element or Locator.')
}

const kLocator = Symbol.for('$$vitest:locator')

export function isLocator(element: unknown): element is Locator {
  return (!!element && typeof element === 'object' && kLocator in element)
}

const DEFAULT_WHEEL_DELTA = 100

export function resolveUserEventWheelOptions(options: UserEventWheelOptions): UserEventWheelDeltaOptions {
  let delta: UserEventWheelDeltaOptions['delta']

  if (options.delta) {
    delta = options.delta
  }
  else {
    switch (options.direction) {

View on GitHub (pinned to 1fa9837ec2)