jestjs/jest · error · Error
${expectedArgument} must be a positive integer
Error message
${expectedArgument} must be a positive integer What it means
Thrown by `toHaveBeenNthCalledWith` (spyMatchers.ts:950) when the `n` argument (the 1-based call index) is not a positive safe integer. The matcher indexes into `mock.calls[nth - 1]`, so values like 0, -1, 1.5, NaN, Infinity, or a string would produce invalid indexing. Jest uses `Number.isSafeInteger(nth) && nth >= 1` as the gate and renders the message with `expectedArgument = 'n'`, so the rendered message reads `n must be a positive integer`.
Source
Thrown at packages/expect/src/spyMatchers.ts:969
[number, ...Array<unknown>]
> =>
function (received: any, nth, ...expected): SyncExpectationResult {
const expectedArgument = 'n';
const options: MatcherHintOptions = {
expectedColor: (arg: string) => arg,
isNot: this.isNot,
promise: this.promise,
secondArgument: '...expected',
};
ensureMockOrSpy(
received,
'toHaveBeenNthCalledWith',
expectedArgument,
options,
);
if (!Number.isSafeInteger(nth) || nth < 1) {
throw new Error(
matcherErrorMessage(
matcherHint(
'toHaveBeenNthCalledWith',
undefined,
expectedArgument,
options,
),
`${expectedArgument} must be a positive integer`,
printWithType(expectedArgument, nth, stringify),
),
);
}
const receivedIsSpy = isSpy(received);
const receivedName = receivedIsSpy ? 'spy' : received.getMockName();
const calls = receivedIsSpy
? received.calls.all().map((x: any) => x.args)View on GitHub (pinned to f49721c78e)
Solutions
- Use 1-based indexing: the first call is `n = 1`, not 0.
- If deriving from `findIndex`, add 1: `.toHaveBeenNthCalledWith(idx + 1, ...)`.
- For the last call, prefer `toHaveBeenLastCalledWith(...)` to avoid index math.
- Validate `n` is a positive integer before the assertion if it is dynamic.
Example fix
// before const idx = mock.calls.findIndex(c => c[0] === 'x'); expect(mock).toHaveBeenNthCalledWith(idx, 'x', payload); // after const idx = mock.calls.findIndex(c => c[0] === 'x'); expect(mock).toHaveBeenNthCalledWith(idx + 1, 'x', payload);
Defensive patterns
Strategy: validation
Validate before calling
if (!Number.isSafeInteger(n) || n < 1) {
throw new Error(`n must be a positive integer, got ${n}`);
}
expect(fn).toHaveBeenNthCalledWith(n, ...args); Type guard
const isPositiveInteger = (v: unknown): v is number => typeof v === 'number' && Number.isSafeInteger(v) && v >= 1;
Try / catch
try {
expect(fn).toHaveBeenNthCalledWith(n, ...args);
} catch (e) {
if (e instanceof Error && /must be a positive integer/.test(e.message)) {
console.error('n was', n, '(calls are 1-indexed)');
}
throw e;
} Prevention
- Remember calls are 1-indexed: the first call is `n = 1`.
- Add 1 to results of `findIndex` before passing as `n`.
- Prefer `toHaveBeenLastCalledWith` when targeting the final call.
When it happens
Trigger: Calling `expect(fn).toHaveBeenNthCalledWith(0, ...)` (zero is invalid — calls are 1-indexed), `.toHaveBeenNthCalledWith(-1, ...)`, `.toHaveBeenNthCalledWith(1.5, ...)`, `.toHaveBeenNthCalledWith('1', ...)` (string), `.toHaveBeenNthCalledWith(NaN, ...)`, or `.toHaveBeenNthCalledWith(calls.length, ...)` intending the last call (off-by-one — should be `calls.length` for the Nth, which is correct, but `calls.length - 1` would be off).
Common situations: Off-by-one errors assuming 0-based indexing; passing a computed index from `findIndex` (0-based) directly instead of adding 1; dynamic index from config that defaulted to 0; porting from a custom matcher that was 0-based.
Related errors
- received value must be a mock or spy function
- received value must be a mock function
- expected path must not be an empty array
- expected value must be a number
- received value must be a number
AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03).
Data as JSON: /data/errors/c6bce63cfd6ce87c.json.
Report an issue: GitHub.