jestjs/jest · error · TypeError

Invalid second argument, ${fn}. It must be a callback functi

Error message

Invalid second argument, ${fn}. It must be a callback function.

What it means

it()/test()'s second argument must be a function. Env.ts:559-562 throws a TypeError when fn is defined but not a function (e.g. an object, array, config, or value). The interpolated fn in the message shows exactly what was passed.

Source

Thrown at packages/jest-jasmine2/src/jasmine/Env.ts:560

          reporter.specDone(result);
        }

        function specStarted(spec: Spec) {
          currentSpec = spec;
          defaultResourcesForRunnable(spec.id, suite.id);
          reporter.specStarted(spec.result);
        }
      };

      this.it = function (description, fn, timeout) {
        description = convertDescriptorToString(description);
        if (fn === undefined) {
          throw new Error(
            'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.',
          );
        }
        if (typeof fn !== 'function') {
          throw new TypeError(
            `Invalid second argument, ${fn}. It must be a callback function.`,
          );
        }
        const spec = specFactory(
          description,
          fn,
          currentDeclarationSuite,
          timeout,
        );
        if (currentDeclarationSuite.markedPending) {
          spec.pend();
        }

        // When a test is defined inside another, jasmine will not run it.
        // This check throws an error to warn the user about the edge-case.
        if (currentSpec !== null) {
          throw new Error(
            `Tests cannot be nested. Test "${spec.description}" cannot run because it is nested within "${currentSpec.description}".`,

View on GitHub (pinned to f49721c78e)

Solutions

  1. Pass a function: it('name', () => { ... }).
  2. Use the interpolated value in the error to identify the wrong binding and fix it.
  3. For options like timeout, pass them as the third argument: it('name', fn, 1000).

Example fix

// before — options object passed as body
it('calculates tax', { timeout: 1000 });

// after — function body, timeout as 3rd arg
it('calculates tax', () => { expect(calc(10)).toBe(1); }, 1000);
Defensive patterns

Strategy: type-guard

Validate before calling

function assertItFn(fn: unknown) {
  if (typeof fn !== 'function') throw new TypeError('it callback must be a function');
}

Type guard

function isFn(v: unknown): v is Function { return typeof v === 'function'; }
if (!isFn(fn)) throw new TypeError('it needs a function callback');

Prevention

When it happens

Trigger: Calling `it('name', { skip: true })`, `it('name', 42)`, or otherwise passing a non-function. The `typeof fn !== 'function'` check at Env.ts:559 fires the TypeError at line 560.

Common situations: Passing an options object meant for a custom wrapper; destructuring bug forwarding the wrong value; refactoring a parametrized test incorrectly.

Related errors


AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03). Data as JSON: /data/errors/30c29fee3aded893.json. Report an issue: GitHub.