vitest-dev/vitest · error · Error

Test function is not found. Did you add it using `setFn`?

Error message

Test function is not found. Did you add it using `setFn`?

What it means

Thrown during test execution when the test task has no callable function attached and the runner does not provide a custom runTask. Every test must have its function registered via setFn during collection; a missing fn usually means the test was registered in a way that skipped function assignment. This is almost always an internal/collector bug rather than a user typo.

Solutions

  1. Ensure every test() call includes a function callback (not just a name).
  2. If using custom collectors, call setFn(test, fn) explicitly after creating the task.
  3. If you implement runner.runTask, you bypass this check; otherwise the fn is required.

Example fix

// before (custom collector)
const t = createTest({ name: 'x' })
// forgot to set fn

// after
setFn(t, () => { expect(true).toBe(true) })
Defensive patterns

Strategy: validation

Validate before calling

function assertTestHasFn(test: Test) {
  if (!getFn(test)) throw new Error(`Test ${test.name} has no function`)
}

Prevention

When it happens

Trigger: A custom runner/collector that creates a test task without calling setFn; programmatically constructing tasks and forgetting the function; a plugin that intercepts test registration and drops the fn.

Common situations: Writing a custom test API that wraps test() but fails to forward the function; using low-level collector APIs incorrectly; a broken transform that strips the test body.

Related errors


AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11). Data as JSON: /api/errors/997ee2934cd76e77. Report an issue: GitHub.

Appendix: source

Thrown at packages/vitest/src/runtime/runner/run.ts:649

          })

          test.result!.repeatCount = repeatCount

          beforeEachCleanups = await $('test.beforeEach', () => callSuiteHook(
            suite,
            test,
            'beforeEach',
            runner,
            [test.context, suite],
          ))

          if (runner.runTask) {
            await $('test.callback', () => limitMaxConcurrency(() => runner.runTask!(test)))
          }
          else {
            const fn = getFn(test)
            if (!fn) {
              throw new Error(
                'Test function is not found. Did you add it using `setFn`?',
              )
            }
            await $('test.callback', () => limitMaxConcurrency(() => fn()))
          }

          await runner.onAfterTryTask?.(test, {
            retry: retryCount,
            repeats: repeatCount,
          })

          if (test.result!.state !== 'fail') {
            test.result!.state = 'pass'
          }
        }
        catch (e) {
          failTask(test.result!, e, runner.config._diffOptions)
        }

View on GitHub (pinned to 1fa9837ec2)