vitest-dev/vitest · error · Error

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

Error message

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

What it means

`toThrowErrorMatchingSnapshot` / `toThrowErrorMatchingInlineSnapshot` call `getError(expected, promise)` which expects the assertion's subject to be a function it can invoke to capture the thrown error. When not in a `resolves`/`rejects` promise context and the subject is not a function, there is nothing to execute, so it throws `expected must be a function, received <type>`.

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

Solutions

  1. Wrap the code that should throw in a function: `expect(() => risky()).toThrowErrorMatchingSnapshot()`.
  2. For async rejection snapshots, use `await expect(promise).rejects.toThrowErrorMatchingSnapshot()` so the promise path is used.
  3. Ensure the matcher name actually matches your intent — use `toMatchSnapshot()` for non-throw values.

Example fix

// before
expect(loadUser(-1)).toThrowErrorMatchingSnapshot()
// after
expect(() => loadUser(-1)).toThrowErrorMatchingSnapshot()
Defensive patterns

Strategy: type-guard

Validate before calling

function assertThrowsSnapshot(fn: () => unknown) {
  if (typeof fn !== 'function') throw new TypeError('expected must be a function')
  expect(fn).toThrowErrorMatchingSnapshot()
}

Type guard

function isCallable(v: unknown): v is (...a: any[]) => any { return typeof v === 'function' }

Prevention

When it happens

Trigger: Writing `expect(<non-function>).toThrowErrorMatchingSnapshot()` / `...InlineSnapshot()` outside of an async `resolves`/`rejects` chain — e.g. `expect(42).toThrowErrorMatchingSnapshot()` or `expect('err').toThrowErrorMatchingInlineSnapshot()`.

Common situations: Forgetting to wrap the throwing call in a function, or confusing `toThrowError*` matchers (which need a callable) with value matchers.

Related errors


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