vitest-dev/vitest · error · Error
Invalid element or locator: ${elementOrLocator}. Expected an
Error message
Invalid element or locator: ${elementOrLocator}. Expected an instance of HTMLElement, SVGElement or Locator, received ${getType(elementOrLocator)} What it means
Thrown by the element() helper used by expect.element() (packages/browser/src/client/tester/expect-element.ts:14-17) when the argument is non-null but is not an HTMLElement, an SVGElement, or a Locator (detected by the Symbol.for('$$vitest:locator') marker). expect.element is the entry point for DOM matchers like toHaveText, toBeVisible, toBeInTheDocument, so a wrong type fails fast before polling begins.
Source
Thrown at packages/browser/src/client/tester/expect-element.ts:16
import type { Assertion, ExpectPollOptions } from 'vitest'
import type { Locator } from 'vitest/browser'
import type { BrowserTraceEntryStatus } from './trace'
import { chai, expect } from 'vitest'
import { getType } from 'vitest/internal/browser'
import { getBrowserState, getWorkerState, now } from '../utils'
import { ariaMatchers } from './aria'
import { matchers } from './expect'
import { processTimeoutOptions } from './tester-utils'
import { createBrowserTraceRangeId, recordBrowserTraceEntry } from './trace'
const kLocator = Symbol.for('$$vitest:locator')
function element<T extends HTMLElement | SVGElement | null | Locator>(elementOrLocator: T, options?: ExpectPollOptions): Assertion<Promise<void>, HTMLElement | SVGElement | null> {
if (elementOrLocator != null && !(elementOrLocator instanceof HTMLElement) && !(elementOrLocator instanceof SVGElement) && !(kLocator in elementOrLocator)) {
throw new Error(`Invalid element or locator: ${elementOrLocator}. Expected an instance of HTMLElement, SVGElement or Locator, received ${getType(elementOrLocator)}`)
}
const pollOptions = processTimeoutOptions(options)
const deadline = pollOptions?.timeout ? now() + pollOptions.timeout : undefined
const expectElement = expect.poll(async function element(this: object): Promise<HTMLElement | SVGElement | null> {
if (elementOrLocator instanceof Element || elementOrLocator == null) {
return elementOrLocator
}
const isNot = chai.util.flag(this, 'negate') as boolean
const name = chai.util.flag(this, '_name') as string
// special case for `toBeInTheDocument` matcher
if (isNot && name === 'toBeInTheDocument') {
return elementOrLocator.query()
}
if (name === 'toHaveLength') {
// we know that `toHaveLength` requires multiple elements,
// but types generally expect a single oneView on GitHub (pinned to d568f8ce37)
Solutions
- Pass a single Element or a Vitest Locator: expect.element(page.getByText('Hi')) or expect.element(node).
- If you have an array of locators, index first: expect.element(locators[0]).
- For raw selectors, build a locator first: expect.element(page.locator('.btn')).
Example fix
// before
expect.element('.save-button').toHaveText('Save')
// after
expect.element(page.locator('.save-button')).toHaveText('Save') Defensive patterns
Strategy: type-guard
Validate before calling
import type { Locator } from 'vitest/browser'
const kLocator = Symbol.for('$$vitest:locator')
function isExpectableElement(v: unknown): v is HTMLElement | SVGElement | Locator {
return v == null
|| v instanceof HTMLElement
|| v instanceof SVGElement
|| (v != null && typeof v === 'object' && kLocator in (v as object))
}
if (isExpectableElement(target)) {
expect.element(target as any)
} Type guard
function isElementOrLocator(v: unknown): v is HTMLElement | SVGElement | Locator {
if (v == null) return true
if (v instanceof HTMLElement || v instanceof SVGElement) return true
return typeof v === 'object' && Symbol.for('$$vitest:locator') in (v as object)
} Prevention
- Only pass HTMLElement, SVGElement, or a Vitest Locator to expect.element.
- Index locator arrays before asserting: expect.element(locators[0]).
When it happens
Trigger: Calling expect.element(value) where value is a string selector, a plain object, a number, a NodeList, or a component wrapper that doesn't expose the locator symbol. Common with expect.element(locator.findAll()) (returns array) instead of a single locator.
Common situations: Mixing testing-library queries (return elements) with Vitest Locator API; passing a CSS selector string instead of a locator; wrapping a locator in another object.
Related errors
- .toContainHTML() expects a string value, got ${htmlText}
- aria adapter expects an Element
- Method "getByRole" is not supported by the "${provider}" pro
- Method "getByLabelText" is not supported by the "${provider}
- Method "getByTestId" is not supported by the "${provider}" p
AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03).
Data as JSON: /data/errors/646f8d5bf7ce2ca4.json.
Report an issue: GitHub.