{"record":{"id":"7479ba237404a83f","repo":"vitest-dev/vitest","slug":"vi-when-times-option-must-be-greater-than-0","errorCode":null,"errorMessage":"vi.when: `times` option must be greater than 0","messagePattern":"vi\\.when: `times` option must be greater than 0","errorType":"exception","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/integrations/mock/when.ts","lineNumber":547,"sourceCode":"      : `exhausted (${action.times} of ${action.times})`\n  }\n\n  return action.remaining === Number.POSITIVE_INFINITY\n    ? 'never called'\n    : `${action.remaining} remaining (out of ${action.times})`\n}\n\nfunction getSymbol(action: BehaviorAction<unknown>): string {\n  if (hasBeenConsumed(action)) {\n    return '✓'\n  }\n\n  return '✗'\n}\n\nfunction validateOptions(options: BehaviorOptions | undefined) {\n  if (typeof options?.times === 'number' && options.times <= 0) {\n    throw new RangeError('vi.when: `times` option must be greater than 0')\n  }\n}\n","sourceCodeStart":529,"sourceCodeEnd":550,"githubUrl":"https://github.com/vitest-dev/vitest/blob/1fa9837ec26533512fdcad8baebf249771bd340a/packages/vitest/src/integrations/mock/when.ts#L529-L550","documentation":"Thrown by `validateOptions` (called by each `then*`/`then*Once` builder) when the `times` option is a number less than or equal to zero. `times` controls how many calls a behavior applies to before being exhausted; zero or negative counts are nonsensical, so a RangeError is raised immediately at setup time rather than silently producing an always-exhausted behavior.","triggerScenarios":"Calling `.thenReturn(value, { times: 0 })`, `.thenReturn(value, { times: -1 })`, or passing a computed `times` that underflows to zero/negative (e.g. `times: arr.length` on an empty array).","commonSituations":"Deriving `times` from a dynamic count (array length, counter) that can be zero; copy-paste of `times: 0` intending 'once' (should be `1` or `thenReturnOnce`); off-by-one in loop-driven stubbing.","solutions":["Use a positive integer: `times: 1` (or prefer `thenReturnOnce` for the single-call case).","Guard dynamic counts: `times: Math.max(1, n)` or skip setting up the behavior when `n === 0`.","Use `thenReturnOnce` instead of `times: 1` for clarity when you want exactly one application."],"exampleFix":"// before\nvi.when(spy).calledWith(1).thenReturn('a', { times: 0 })\n\n// after\nvi.when(spy).calledWith(1).thenReturnOnce('a')","handlingStrategy":"validation","validationCode":"function validTimes(n) {\n  if (typeof n === 'number' && n <= 0) throw new RangeError('times must be > 0')\n  return n\n}\nvi.when(spy).calledWith(1).thenReturn('a', { times: validTimes(n) })","typeGuard":"function isPositiveTimes(n): n is number {\n  return typeof n === 'number' && n > 0\n}","tryCatchPattern":null,"preventionTips":["Prefer thenReturnOnce over { times: 1 } for single-shot behaviors.","Guard dynamic counts with Math.max(1, n) or skip setup when n === 0.","Validate computed times values before passing them to then* methods."],"tags":["vi-when","times-option","validation","range-error"],"backgroundTag":null,"analyzedSha":"1fa9837ec26533512fdcad8baebf249771bd340a","analyzedAt":"2026-08-11T16:11:39.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}