{"record":{"id":"513e08af97aed934","repo":"vitest-dev/vitest","slug":"utils-inspect-chain-is-not-a-vi-when-instanc","errorCode":null,"errorMessage":"${utils.inspect(chain)} is not a `vi.when` instance","messagePattern":"(.+?) is not a `vi\\.when` instance","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/integrations/mock/chai.ts","lineNumber":13,"sourceCode":"import type { ChaiPlugin } from '@vitest/expect'\nimport { wrapAssertion } from '@vitest/expect'\nimport { isWhenChain } from './when'\n\nexport const MockPlugin: ChaiPlugin = (chai, utils) => {\n  utils.addMethod(\n    chai.Assertion.prototype,\n    'toHaveBeenExhausted',\n    wrapAssertion(utils, 'toHaveBeenExhausted', function (this) {\n      const chain = utils.flag(this, 'object')\n\n      if (!isWhenChain(chain)) {\n        throw new TypeError(\n          `${utils.inspect(chain)} is not a \\`vi.when\\` instance`,\n        )\n      }\n\n      const diagnostics = chain._getDiagnostics()\n\n      this.assert(\n        diagnostics.isExhausted,\n        `expected all behaviors to have been exhausted, but some remain:\\n\\n  ${diagnostics.pendingBehaviors.replaceAll(/\\n(?!\\n)/g, '\\n  ')}`,\n        'expected at least one behavior to remain un-exhausted, but all were',\n      )\n    }),\n  )\n}\n","sourceCodeStart":1,"sourceCodeEnd":28,"githubUrl":"https://github.com/vitest-dev/vitest/blob/1fa9837ec26533512fdcad8baebf249771bd340a/packages/vitest/src/integrations/mock/chai.ts#L1-L28","documentation":"Thrown by the `toHaveBeenExhausted` Chai assertion when the asserted value is not a chain returned by `vi.when(...)`. The assertion inspects the object via `isWhenChain` (which checks for the internal `$$vitest:when` symbol); if the symbol is absent it raises a TypeError. It exists to stop developers from calling the assertion on a plain mock, spy, or unrelated value, since exhaustion tracking is only meaningful for `vi.when` behaviors.","triggerScenarios":"Calling `expect(spy).toHaveBeenExhausted()` where `spy` is a `vi.fn()`/`vi.spyOn()` result instead of the object returned by `vi.when(spy)`; calling it on a mock that was never passed through `vi.when`; passing a non-mock value like a plain object or number.","commonSituations":"Confusing the mock function with the `vi.when` return value; refactoring code so the `when` result is no longer in scope; copy-pasting an assertion without setting up `vi.when`.","solutions":["Capture the return of `vi.when(spy)` and assert against that: `const w = vi.when(spy); ...; expect(w).toHaveBeenExhausted()`.","If you intended to check call counts instead, use `expect(spy).toHaveBeenCalledTimes(n)`.","Remove the `toHaveBeenExhausted` assertion if no `vi.when` behavior was set up."],"exampleFix":"// before\nconst spy = vi.fn()\nvi.when(spy).calledWith(1).thenReturn('a')\nexpect(spy).toHaveBeenExhausted()\n\n// after\nconst spy = vi.fn()\nconst w = vi.when(spy).calledWith(1).thenReturn('a')\nexpect(w).toHaveBeenExhausted()","handlingStrategy":"type-guard","validationCode":"import { isWhenChain } from 'vitest/vi' // or from the mock internals\nif (!isWhenChain(target)) {\n  throw new Error('Pass the vi.when(...) return value, not the mock itself')\n}\nexpect(target).toHaveBeenExhausted()","typeGuard":"import { isWhenChain } from '@vitest/spy'\nfunction assertWhenChain(v: unknown) {\n  if (!isWhenChain(v as object)) throw new TypeError('not a vi.when instance')\n  return v\n}","tryCatchPattern":"try {\n  expect(target).toHaveBeenExhausted()\n} catch (e) {\n  if (e instanceof TypeError && /vi\\.when/.test(e.message)) {\n    // re-route: capture the vi.when return value before asserting\n  }\n  throw e\n}","preventionTips":["Always assign the return of vi.when(spy) to a variable and assert against that variable.","Add a lint rule or code-review check that toHaveBeenExhausted is only called on a vi.when result.","Write a small wrapper that accepts the when-chain and throws a clearer error if a non-chain is passed."],"tags":["vi-when","chai-assertion","mocking","type-guard"],"backgroundTag":null,"analyzedSha":"1fa9837ec26533512fdcad8baebf249771bd340a","analyzedAt":"2026-08-11T16:11:39.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}