vitest-dev/vitest · error · Error

Hook ${name}() can only be called inside a test

Error message

Hook ${name}() can only be called inside a test

What it means

`onTestFailed` and `onTestFinished` are test-scoped hooks created via `createTestHook` (hooks.ts:444). They require an active test (`getCurrentTest()` must be truthy) at call time. If called outside any test body — at the top level, inside `describe`, inside `beforeAll`/`beforeEach`, or after the test has finished — there is no current test to attach the hook to, so the error is thrown with the hook name.

Source

Thrown at packages/vitest/src/runtime/runner/hooks.ts:454

}

export function getAroundHookStackTrace(hook: Function): Error | undefined {
  return AROUND_STACK_TRACE_KEY in hook && hook[AROUND_STACK_TRACE_KEY] instanceof Error
    ? hook[AROUND_STACK_TRACE_KEY]
    : undefined
}

function createTestHook<T>(
  name: string,
  handler: (test: TaskPopulated, handler: T, timeout?: number) => void,
): TaskHook<T> {
  return (fn: T, timeout?: number) => {
    assertTypes(fn, `"${name}" callback`, ['function'])

    const current = getCurrentTest()

    if (!current) {
      throw new Error(`Hook ${name}() can only be called inside a test`)
    }

    return handler(current, fn, timeout)
  }
}

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Move the `onTestFailed`/`onTestFinished` call inside a `test(...)` body.
  2. For suite-level setup/teardown, use `beforeAll`/`afterAll` or `beforeEach`/`afterEach` instead.
  3. Ensure the hook is registered synchronously while the test is the current task.

Example fix

// before
describe('suite', () => {
  onTestFailed(() => { /* wrong place */ })
  test('x', () => {})
})
// after
describe('suite', () => {
  test('x', () => {
    onTestFailed(() => { /* correct */ })
  })
})
Defensive patterns

Strategy: validation

Validate before calling

// Structural check: ensure onTestFailed/onTestFinished calls are lexically inside a test.
// In practice, rely on the runtime check; statically, forbid these calls in hooks via lint:
// 'no-restricted-syntax' banning onTestFailed/onTestFinished inside beforeEach/afterEach/beforeAll/afterAll blocks.

Prevention

When it happens

Trigger: Calling `onTestFailed(fn)` at module top level; calling it inside a `describe` factory body directly (not inside a test); calling `onTestFinished` inside `beforeEach`/`beforeAll` suite hooks; calling after the current test already completed.

Common situations: Placing cleanup handlers at the wrong nesting level; copy-pasting a hook out of a test; refactoring that moves a hook call into a suite-level setup.

Related errors


AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03). Data as JSON: /data/errors/2deacf699ecb7128.json. Report an issue: GitHub.