vitest-dev/vitest · error · TypeError
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
`toHaveBeenCalledBefore(otherSpy)` validates that its argument is itself a spy before comparing invocation order. If the argument cannot act as a mock, it throws this `TypeError`. The matcher cannot read call data from a non-mock, so it aborts before ordering checks.
Solutions
- Ensure the argument is created with `vi.fn()` or `vi.spyOn()` before being passed in.
- Pass the mock instance itself, not a result or call object.
- Double-check that the same reference is used in the SUT and the assertion.
Example fix
// before
const a = vi.fn()
const b = () => {}
expect(a).toHaveBeenCalledBefore(b)
// after
const a = vi.fn()
const b = vi.fn()
expect(a).toHaveBeenCalledBefore(b) Defensive patterns
Strategy: type-guard
Validate before calling
import { isMockFunction } from '@vitest/spy'
if (!isMockFunction(other)) { throw new Error('argument must be a vi.fn spy') }
expect(spyA).toHaveBeenCalledBefore(other) Type guard
const isSpy = (v: unknown): v is import('vitest').Mock =>
!!v && typeof v === 'function' && 'mock' in (v as any) Prevention
- Wrap both collaborators with vi.fn before comparing call order.
- Share the same spy reference between setup and assertion.
- Type the helper parameter as Mock so non-spies are caught at compile time.
When it happens
Trigger: Calling `expect(spyA).toHaveBeenCalledBefore(plainFn)` where `plainFn` was never wrapped with `vi.fn()`/`vi.spyOn()`; passing a mock's return value or a function reference that lost its mock metadata.
Common situations: Comparing two collaborators but forgetting to wrap the second; refactoring that replaced a `vi.fn()` with a normal function; asserting against an imported function that was not mocked.
Related errors
- is not a spy or a call to a spy!
- expect.poll() is not supported in combination with .resolves
- expect.poll() is not supported in combination with .rejects
- expected function to throw an error, but it didn't
- .toHaveProperty() expects to receive a valid object, but got
AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11).
Data as JSON: /api/errors/6e3b81876c4b7ac9.
Report an issue: GitHub.
Appendix: source
Thrown at packages/expect/src/jest-expect.ts:707
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 1fa9837ec2)