jestjs/jest · error · Error

must be a positive integer

Error message

${expectedArgument} must be a positive integer

What it means

Thrown by `toHaveBeenNthCalledWith` (spyMatchers.ts:968) when the `nth` argument is not a safe positive integer. The matcher checks `Number.isSafeInteger(nth) && nth >= 1` because call indices are 1-based; zero, negatives, NaN, Infinity, fractions, and non-numbers are all invalid.

Solutions

  1. Use 1-based indexing: the first call is nth=1, not nth=0.
  2. If deriving from a 0-based index, add 1: toHaveBeenNthCalledWith(i + 1, ...).
  3. Ensure nth is a safe integer (not NaN/Infinity/fraction) before the call.
  4. For the last call prefer toHaveBeenLastCalledWith; for the only call just toHaveBeenCalledWith.

Example fix

// before
expect(fn).toHaveBeenNthCalledWith(0, 'first');
expect(fn).toHaveBeenNthCalledWith(i, args); // i is 0-based

// after
expect(fn).toHaveBeenNthCalledWith(1, 'first');
expect(fn).toHaveBeenNthCalledWith(i + 1, args);
Defensive patterns

Strategy: validation

Validate before calling

const nth = index;
if (!Number.isSafeInteger(nth) || nth < 1) {
  throw new Error('toHaveBeenNthCalledWith nth must be a positive integer (1-based)');
}
expect(fn).toHaveBeenNthCalledWith(nth, ...args);

Type guard

const isPositiveInteger = (v: unknown): v is number =>
  typeof v === 'number' && Number.isSafeInteger(v) && v >= 1;

Prevention

When it happens

Trigger: Calling `expect(fn).toHaveBeenNthCalledWith(0, ...)` (zero, not 1-based), a negative, a float like 1.5, NaN, a string like '1', undefined, or a computed index that is off-by-one against the 1-based convention.

Common situations: Off-by-one from assuming 0-based indexing (most common); a loop variable or array length used directly as nth; nth derived from a 0-based findIndex result without +1; passing an optional that was undefined.

Related errors


AI-assisted analysis of jestjs/jest@8e6d128e4a (2026-08-10). Data as JSON: /api/errors/c6bce63cfd6ce87c. Report an issue: GitHub.

Appendix: 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 8e6d128e4a)