vitest-dev/vitest · error · TypeError

${utils.inspect(resultSpy)} is not a spy or a call to a spy

Error message

${utils.inspect(resultSpy)} is not a spy or a call to a spy

What it means

TypeError thrown by toHaveBeenCalledBefore when the second argument (resultSpy) is not a mock function. Unlike the receiver (which goes through getSpy and produces error 104), the comparison target is checked separately with isMockFunction and rejected with a distinct message (no trailing '!').

Source

Thrown at packages/expect/src/jest-expect.ts:702

    if (beforeInvocationCallOrder.length === 0) {
      return !failIfNoFirstInvocation
    }

    if (afterInvocationCallOrder.length === 0) {
      return false
    }

    return beforeInvocationCallOrder[0] < afterInvocationCallOrder[0]
  }

  def(
    ['toHaveBeenCalledBefore'],
    function (resultSpy: MockInstance, failIfNoFirstInvocation = true) {
      const expectSpy = getSpy(this)

      if (!isMockFunction(resultSpy)) {
        throw new TypeError(
          `${utils.inspect(resultSpy)} is not a spy or a call to a spy`,
        )
      }

      this.assert(
        isSpyCalledBeforeAnotherSpy(
          expectSpy,
          resultSpy,
          failIfNoFirstInvocation,
        ),
        `expected "${expectSpy.getMockName()}" to have been called before "${resultSpy.getMockName()}"`,
        `expected "${expectSpy.getMockName()}" to not have been called before "${resultSpy.getMockName()}"`,
        resultSpy,
        expectSpy,
      )
    },
  )
  def(

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Wrap the second function in vi.fn or vi.spyOn before passing it: const spyB = vi.spyOn(obj, 'bar'); expect(spyA).toHaveBeenCalledBefore(spyB).
  2. Confirm you are passing the spy reference, not the spy's result or mock property.

Example fix

// before
expect(spyA).toHaveBeenCalledBefore(obj.bar);
// after
const spyB = vi.spyOn(obj, 'bar');
expect(spyA).toHaveBeenCalledBefore(spyB);
Defensive patterns

Strategy: type-guard

Validate before calling

import { isMockFunction } from '@vitest/spy';
if (!isMockFunction(resultSpy)) throw new TypeError('resultSpy is not a mock — wrap it with vi.fn()/vi.spyOn()');

Type guard

const isMock = (v: unknown): v is MockInstance => isMockFunction(v);

Prevention

When it happens

Trigger: expect(spyA).toHaveBeenCalledBefore(plainFunction); expect(spyA).toHaveBeenCalledBefore({}); expect(spyA).toHaveBeenCalledBefore(undefined). Any call where the resultSpy parameter fails isMockFunction.

Common situations: Passing the unwrapped method instead of the spy, e.g. expect(spyOnFoo).toHaveBeenCalledBefore(obj.bar) where obj.bar was never spied on. Or passing a return value of spyOn instead of the spy itself.

Related errors


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