vitest-dev/vitest · error · Error

expected must be a function, received

Error message

expected must be a function, received ${typeof expected}

What it means

Thrown inside the snapshot helper `getError` when the value handed to `toThrowErrorMatchingSnapshot` / `toThrowErrorMatchingInlineSnapshot` is not a function AND the assertion is not in promise mode (i.e. no `rejects`/`throws` promise flag). In the non-promise path Vitest expects a callable that throws, so a non-function value is a usage error.

Solutions

  1. Wrap the throwing code in a function: `expect(() => { throw new Error('x') }).toThrowErrorMatchingSnapshot()`.
  2. If asserting on a rejected promise, use the promise form: `await expect(promise).rejects.toThrowErrorMatchingSnapshot()`.
  3. Double-check you are not passing an already-caught error object.

Example fix

// before
const err = new Error('boom')
expect(err).toThrowErrorMatchingSnapshot()

// after
expect(() => { throw new Error('boom') }).toThrowErrorMatchingSnapshot()
Defensive patterns

Strategy: type-guard

Validate before calling

function snapshotThrow(fn) {
  if (typeof fn !== 'function') throw new Error('pass a function that throws')
  return expect(fn).toThrowErrorMatchingSnapshot()
}

Type guard

function isThrowingFn(v): v is () => void {
  return typeof v === 'function'
}

Prevention

When it happens

Trigger: Writing `expect(myError).toThrowErrorMatchingSnapshot()` (passing an Error instance directly) instead of `expect(() => { throw myError }).toThrowErrorMatchingSnapshot()`; passing a string or object literal to the assertion; using the assertion on a non-throwing expression.

Common situations: Confusing `expect(...).toThrow()` style (which wraps a function) with passing the error itself; refactor that moved the throw out of an arrow function; copy-pasting from `toThrow` examples without the function wrapper.

Related errors


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

Appendix: source

Thrown at packages/vitest/src/integrations/snapshot/chai.ts:30

import { getNames } from '../../utils/tasks'

let _client: SnapshotClient

export function getSnapshotClient(): SnapshotClient {
  if (!_client) {
    _client = new SnapshotClient({
      isEqual: (received, expected) => {
        return equals(received, expected, [iterableEquality, subsetEquality])
      },
    })
  }
  return _client
}

function getError(expected: () => void | Error, promise: string | undefined) {
  if (typeof expected !== 'function') {
    if (!promise) {
      throw new Error(
        `expected must be a function, received ${typeof expected}`,
      )
    }

    // when "promised", it receives thrown error
    return expected
  }

  try {
    expected()
  }
  catch (e) {
    return e
  }

  throw new Error('snapshot function didn\'t throw')
}

View on GitHub (pinned to 1fa9837ec2)