{"record":{"id":"6e3b81876c4b7ac9","repo":"vitest-dev/vitest","slug":"utils-inspect-resultspy-is-not-a-spy-or-a-call","errorCode":null,"errorMessage":"${utils.inspect(resultSpy)} is not a spy or a call to a spy","messagePattern":"(.+?) is not a spy or a call to a spy","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/expect/src/jest-expect.ts","lineNumber":707,"sourceCode":"\n    if (beforeInvocationCallOrder.length === 0) {\n      return !failIfNoFirstInvocation\n    }\n\n    if (afterInvocationCallOrder.length === 0) {\n      return false\n    }\n\n    return beforeInvocationCallOrder[0] < afterInvocationCallOrder[0]\n  }\n\n  def(\n    ['toHaveBeenCalledBefore'],\n    function (resultSpy: MockInstance, failIfNoFirstInvocation = true) {\n      const expectSpy = getSpy(this)\n\n      if (!isMockFunction(resultSpy)) {\n        throw new TypeError(\n          `${utils.inspect(resultSpy)} is not a spy or a call to a spy`,\n        )\n      }\n\n      this.assert(\n        isSpyCalledBeforeAnotherSpy(\n          expectSpy,\n          resultSpy,\n          failIfNoFirstInvocation,\n        ),\n        `expected \"${expectSpy.getMockName()}\" to have been called before \"${resultSpy.getMockName()}\"`,\n        `expected \"${expectSpy.getMockName()}\" to not have been called before \"${resultSpy.getMockName()}\"`,\n        resultSpy,\n        expectSpy,\n      )\n    },\n  )\n  def(","sourceCodeStart":689,"sourceCodeEnd":725,"githubUrl":"https://github.com/vitest-dev/vitest/blob/1fa9837ec26533512fdcad8baebf249771bd340a/packages/expect/src/jest-expect.ts#L689-L725","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst a = vi.fn()\nconst b = () => {}\nexpect(a).toHaveBeenCalledBefore(b)\n\n// after\nconst a = vi.fn()\nconst b = vi.fn()\nexpect(a).toHaveBeenCalledBefore(b)","handlingStrategy":"type-guard","validationCode":"import { isMockFunction } from '@vitest/spy'\nif (!isMockFunction(other)) { throw new Error('argument must be a vi.fn spy') }\nexpect(spyA).toHaveBeenCalledBefore(other)","typeGuard":"const isSpy = (v: unknown): v is import('vitest').Mock =>\n  !!v && typeof v === 'function' && 'mock' in (v as any)","tryCatchPattern":null,"preventionTips":["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."],"tags":["expect","mock","spy","call-order","jest-expect"],"backgroundTag":null,"analyzedSha":"1fa9837ec26533512fdcad8baebf249771bd340a","analyzedAt":"2026-08-11T16:11:39.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}