{"id":"997ee2934cd76e77","repo":"vitest-dev/vitest","slug":"test-function-is-not-found-did-you-add-it-using","errorCode":null,"errorMessage":"Test function is not found. Did you add it using `setFn`?","messagePattern":"Test function is not found\\. Did you add it using `setFn`\\?","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/runtime/runner/run.ts","lineNumber":649,"sourceCode":"          })\n\n          test.result!.repeatCount = repeatCount\n\n          beforeEachCleanups = await $('test.beforeEach', () => callSuiteHook(\n            suite,\n            test,\n            'beforeEach',\n            runner,\n            [test.context, suite],\n          ))\n\n          if (runner.runTask) {\n            await $('test.callback', () => limitMaxConcurrency(() => runner.runTask!(test)))\n          }\n          else {\n            const fn = getFn(test)\n            if (!fn) {\n              throw new Error(\n                'Test function is not found. Did you add it using `setFn`?',\n              )\n            }\n            await $('test.callback', () => limitMaxConcurrency(() => fn()))\n          }\n\n          await runner.onAfterTryTask?.(test, {\n            retry: retryCount,\n            repeats: repeatCount,\n          })\n\n          if (test.result!.state !== 'fail') {\n            test.result!.state = 'pass'\n          }\n        }\n        catch (e) {\n          failTask(test.result!, e, runner.config._diffOptions)\n        }","sourceCodeStart":631,"sourceCodeEnd":667,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/vitest/src/runtime/runner/run.ts#L631-L667","documentation":"During `runTest` (run.ts:647-652), if the runner has no custom `runTask`, Vitest fetches the test's function via `getFn(test)`. If no function was registered for the task (via `setFn`), it throws this error pointing to `setFn`. This indicates the task was collected without an executable body — typically a setup/registration bug rather than a user test error.","triggerScenarios":"A custom runner or plugin that creates a `Test` task and calls `runTest` without first registering a function via `setFn`; manually constructing tasks; a collection path that skips `setFn` assignment.","commonSituations":"Custom Vitest runner implementations; programmatic test creation; bugs in test-collection middleware; running a `todo` task that has no body but reached execution.","solutions":["Ensure `setFn(task, fn)` is called for every test task before it runs.","Use the standard `test(...)` API which registers the function automatically.","If using a custom runner, implement `runner.runTask` or guarantee `setFn` is invoked during collection."],"exampleFix":"// before: custom task without function\nconst task = createTestTask()\nawait runTest(task, runner) // getFn returns undefined\n// after\nsetFn(task, () => { /* test body */ })\nawait runTest(task, runner)","handlingStrategy":"validation","validationCode":"// Custom runner: ensure setFn is called for every task.\nimport { setFn } from 'vitest/runtime'\nfunction ensureFn(task: Test) {\n  if (typeof (task as any).fn !== 'function') {\n    setFn(task, () => { throw new Error(`No body for ${task.name}`) })\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer the standard test()/it() API which registers functions automatically.","In custom runners, always pair task creation with setFn.","Implement runner.runTask if you bypass the default getFn path."],"tags":["runner","setfn","internal","task-execution"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}