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
- Wrap the second function in vi.fn or vi.spyOn before passing it: const spyB = vi.spyOn(obj, 'bar'); expect(spyA).toHaveBeenCalledBefore(spyB).
- 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
- Spy on the second method before passing it as resultSpy.
- Pass the spy reference itself, never its return value.
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
- ${utils.inspect(assertion._obj)} is not a spy or a call to a
- .toMatch() expects to receive a string, but got ${typeof act
- toContain() expected a DOM node as the argument, but got ${t
- ${msg}
- ${formatCalls(spy, msg, args)}
AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03).
Data as JSON: /data/errors/6e3b81876c4b7ac9.json.
Report an issue: GitHub.