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` at tester-utils.ts:310 when the input is truthy but is neither an `instanceof Element` nor passes the `isLocator` duck-type check (Symbol.for('$$vitest:locator') in object). The serializer only accepts real DOM Elements or Vitest Locators.

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 d568f8ce37)

Solutions

  1. Pass a DOM Element obtained via `querySelector`/`getByRole` etc., or a Vitest `Locator`.
  2. Unwrap collections/jQuery: pass `$(sel)[0]` not `$(sel)`.
  3. If using a custom locator abstraction, adapt it to expose the Vitest Locator interface or resolve to an Element first.

Example fix

// before
await serializeElement($('.btn')) // jQuery wrapper

// after
await serializeElement(document.querySelector('.btn')!)
Defensive patterns

Strategy: type-guard

Validate before calling

import { isLocator } from '@vitest/browser/client'
function isSerializable(v: unknown): v is Element | Locator {
  return v instanceof Element || isLocator(v)
}
if (!isSerializable(target)) throw new Error('expected Element or Locator')

Type guard

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

Prevention

When it happens

Trigger: Passing a plain object, a string, a number, a Node that isn't an Element (text/comment node), a Playwright Locator from a different library, or a stale wrapper that lost its symbol.

Common situations: Hand-constructing a locator-like object. Passing a jQuery wrapper or testing-library element instead of a DOM Element. Cross-realm Element instances failing `instanceof Element` (rare; usually caught earlier).

Related errors


AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03). Data as JSON: /data/errors/b15ccffa540eac02.json. Report an issue: GitHub.