{"record":{"id":"2deacf699ecb7128","repo":"vitest-dev/vitest","slug":"hook-name-can-only-be-called-inside-a-test","errorCode":null,"errorMessage":"Hook ${name}() can only be called inside a test","messagePattern":"Hook (.+?)\\(\\) can only be called inside a test","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/runtime/runner/hooks.ts","lineNumber":454,"sourceCode":"}\n\nexport function getAroundHookStackTrace(hook: Function): Error | undefined {\n  return AROUND_STACK_TRACE_KEY in hook && hook[AROUND_STACK_TRACE_KEY] instanceof Error\n    ? hook[AROUND_STACK_TRACE_KEY]\n    : undefined\n}\n\nfunction createTestHook<T>(\n  name: string,\n  handler: (test: TaskPopulated, handler: T, timeout?: number) => void,\n): TaskHook<T> {\n  return (fn: T, timeout?: number) => {\n    assertTypes(fn, `\"${name}\" callback`, ['function'])\n\n    const current = getCurrentTest()\n\n    if (!current) {\n      throw new Error(`Hook ${name}() can only be called inside a test`)\n    }\n\n    return handler(current, fn, timeout)\n  }\n}\n","sourceCodeStart":436,"sourceCodeEnd":460,"githubUrl":"https://github.com/vitest-dev/vitest/blob/1fa9837ec26533512fdcad8baebf249771bd340a/packages/vitest/src/runtime/runner/hooks.ts#L436-L460","documentation":"Thrown by createTestHook, the factory behind onTestFailed and onTestFinished. These hooks bind to the currently executing test, so they require a live test context (getCurrentTest() must return a test). Calling them at module top-level, inside describe/setup hooks, or inside beforeAll/afterAll yields no current test and triggers this error.","triggerScenarios":"Calling `onTestFailed(fn)` or `onTestFinished(fn)` at the top level of a test file; inside a beforeAll/afterAll/beforeEach/afterEach hook body (those run outside the test execution frame); inside describe factory body before any test runs.","commonSituations":"Factoring shared cleanup into a helper that calls onTestFinished unconditionally; pasting test-body code into a hook during refactor; calling onTestFailed from a setup utility module.","solutions":["Move the onTestFailed/onTestFinished call inside the test() callback body.","For shared logic, accept the context as a parameter and call `context.onTestFinished` from within the test.","Use beforeEach/afterEach for setup/teardown that applies to every test rather than onTestFinished."],"exampleFix":"// before\nbeforeEach(() => {\n  onTestFailed(() => cleanup()) // throws: not in a test\n})\n\n// after\ntest('x', () => {\n  onTestFailed(() => cleanup())\n})","handlingStrategy":"validation","validationCode":"import { getCurrentTest } from 'vitest'\nfunction registerAfterTest(fn: () => void) {\n  if (!getCurrentTest()) throw new Error('onTestFinished must be called inside a test')\n  onTestFinished(fn)\n}","typeGuard":"const inTest = () => !!getCurrentTest()","tryCatchPattern":null,"preventionTips":["Only call onTestFailed/onTestFinished from within a test() body.","Pass the test context into helpers rather than calling globals from hooks.","Use beforeEach/afterEach for hook-scoped setup/teardown."],"tags":["hooks","test-context","lifecycle"],"backgroundTag":null,"analyzedSha":"1fa9837ec26533512fdcad8baebf249771bd340a","analyzedAt":"2026-08-11T16:11:39.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}