vitest-dev/vitest · error · TypeError

.toContainHTML() expects a string value, got ${htmlText}

Error message

.toContainHTML() expects a string value, got ${htmlText}

What it means

Thrown by the toContainHTML matcher (packages/browser/src/client/tester/expect/toContainHTML.ts:33-35) when the second argument (the expected HTML snippet) is not a string. The matcher normalizes the snippet via a temp div.innerHTML (line 20-23), which requires a string; numbers, objects, or undefined would either coerce wrongly or break normalization, so the matcher rejects them with a TypeError.

Source

Thrown at packages/browser/src/client/tester/expect/toContainHTML.ts:34

import type { MatcherResult, MatcherState } from 'vitest'
import type { Locator } from '../locators'
import { getElementFromUserInput } from './utils'

function getNormalizedHtml(container: HTMLElement | SVGElement, htmlText: string) {
  const div = container.ownerDocument.createElement('div')
  div.innerHTML = htmlText
  return div.innerHTML
}

export default function toContainHTML(
  this: MatcherState,
  actual: Element | Locator,
  htmlText: string,
): MatcherResult {
  const htmlElement = getElementFromUserInput(actual, toContainHTML, this)

  if (typeof htmlText !== 'string') {
    throw new TypeError(`.toContainHTML() expects a string value, got ${htmlText}`)
  }

  return {
    pass: htmlElement.outerHTML.includes(getNormalizedHtml(htmlElement, htmlText)),
    message: () => {
      return [
        this.utils.matcherHint(
          `${this.isNot ? '.not' : ''}.toContainHTML`,
          'element',
          '',
        ),
        'Expected:',
        `  ${this.utils.EXPECTED_COLOR(htmlText)}`,
        'Received:',
        `  ${this.utils.printReceived(htmlElement.cloneNode(true))}`,
      ].join('\n')
    },
  }

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Pass the expected markup as a string literal or string variable: expect(el).toContainHTML('<span>Hi</span>').
  2. If you have a node, serialize it: expect(el).toContainHTML(otherEl.outerHTML).
  3. Coerce safely only when meaningful: expect(el).toContainHTML(String(value)).

Example fix

// before
expect(container).toContainHTML(childElement)
// after
expect(container).toContainHTML(childElement.outerHTML)
Defensive patterns

Strategy: type-guard

Validate before calling

function ensureHtmlString(v: unknown): string {
  if (typeof v === 'string') return v
  if (v instanceof Element) return v.outerHTML
  throw new TypeError(`toContainHTML expects a string, got ${typeof v}`)
}
expect(container).toContainHTML(ensureHtmlString(maybeNodeOrString))

Type guard

function isHtmlSnippet(v: unknown): v is string {
  return typeof v === 'string'
}

Prevention

When it happens

Trigger: Calling expect(el).toContainHTML(value) where value is a number, an object, null, or undefined — e.g. passing a DOM node instead of its outerHTML string, or passing a variable that was never assigned.

Common situations: Passing an Element where its HTML string was intended (forgot .outerHTML); passing a number from a computed value; refactor that changed the variable type from string to something else.

Related errors


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