vitest-dev/vitest · error · TypeError

${utils.inspect(chain)} is not a `vi.when` instance

Error message

${utils.inspect(chain)} is not a `vi.when` instance

What it means

The `.toHaveBeenExhausted` assertion (added by the mock chai plugin) is specifically for verifying that every behavior registered on a `vi.when(...)` chain has been consumed. It inspects the `$$vitest:when` symbol via `isWhenChain`; if the asserted object is not a `vi.when` instance it throws a `TypeError`. This prevents calling the matcher on a plain mock or arbitrary value where exhaustion is undefined.

Source

Thrown at packages/vitest/src/integrations/mock/chai.ts:13

import type { ChaiPlugin } from '@vitest/expect'
import { wrapAssertion } from '@vitest/expect'
import { isWhenChain } from './when'

export const MockPlugin: ChaiPlugin = (chai, utils) => {
  utils.addMethod(
    chai.Assertion.prototype,
    'toHaveBeenExhausted',
    wrapAssertion(utils, 'toHaveBeenExhausted', function (this) {
      const chain = utils.flag(this, 'object')

      if (!isWhenChain(chain)) {
        throw new TypeError(
          `${utils.inspect(chain)} is not a \`vi.when\` instance`,
        )
      }

      const diagnostics = chain._getDiagnostics()

      this.assert(
        diagnostics.isExhausted,
        `expected all behaviors to have been exhausted, but some remain:\n\n  ${diagnostics.pendingBehaviors.replaceAll(/\n(?!\n)/g, '\n  ')}`,
        'expected at least one behavior to remain un-exhausted, but all were',
      )
    }),
  )
}

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Call `.toHaveBeenExhausted` on the value returned by `vi.when(spy)`, not on the spy itself.
  2. If you wanted to assert call counts on a plain mock, use `.toHaveBeenCalled()` / `.toHaveBeenCalledTimes(n)` instead.
  3. Ensure the `vi.when(...)` chain was actually assigned to a variable before asserting on it.

Example fix

// before
const spy = vi.fn()
vi.when(spy).calledWith(1).thenReturn(0)
expect(spy).toHaveBeenExhausted()
// after
const w = vi.when(spy).calledWith(1).thenReturn(0)
expect(w).toHaveBeenExhausted()
Defensive patterns

Strategy: type-guard

Validate before calling

import { isWhenChain } from 'vitest'
// assert on the vi.when handle, not the spy
const w = vi.when(spy).calledWith(1).thenReturn(0)
if (!isWhenChain(w)) throw new Error('expected a vi.when instance')
expect(w).toHaveBeenExhausted()

Type guard

import { isWhenChain } from 'vitest'
// isWhenChain(input): input is When — exported from vitest

Prevention

When it happens

Trigger: Calling `expect(someValue).toHaveBeenExhausted()` where `someValue` is a `vi.fn()`/`vi.spyOn()` mock, a raw function, an object, or any value not returned by `vi.when(spy)`.

Common situations: Confusing `vi.fn()` with `vi.when()`, or asserting exhaustion on the spy itself rather than the `vi.when` chain handle.

Related errors


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