{"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":702,"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":684,"sourceCodeEnd":720,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/expect/src/jest-expect.ts#L684-L720","documentation":"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 '!').","triggerScenarios":"expect(spyA).toHaveBeenCalledBefore(plainFunction); expect(spyA).toHaveBeenCalledBefore({}); expect(spyA).toHaveBeenCalledBefore(undefined). Any call where the resultSpy parameter fails isMockFunction.","commonSituations":"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.","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."],"exampleFix":"// before\nexpect(spyA).toHaveBeenCalledBefore(obj.bar);\n// after\nconst spyB = vi.spyOn(obj, 'bar');\nexpect(spyA).toHaveBeenCalledBefore(spyB);","handlingStrategy":"type-guard","validationCode":"import { isMockFunction } from '@vitest/spy';\nif (!isMockFunction(resultSpy)) throw new TypeError('resultSpy is not a mock — wrap it with vi.fn()/vi.spyOn()');","typeGuard":"const isMock = (v: unknown): v is MockInstance => isMockFunction(v);","tryCatchPattern":null,"preventionTips":["Spy on the second method before passing it as resultSpy.","Pass the spy reference itself, never its return value."],"tags":["typeerror","mock","spy","tohavebeencalledbefore","type-mismatch"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}