vitest-dev/vitest · error · Error

Cannot call "onTestFailed" inside a test hook.

Error message

Cannot call "onTestFailed" inside a test hook.

What it means

While `beforeEach`/`afterEach` (and onFinished/onFailed) hooks execute, Vitest temporarily replaces `context.onTestFailed` with a function that throws this error (run.ts:138-140). This prevents registering a failure handler for an already-finishing test from within a hook, where the registration semantics are ambiguous.

Source

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

  runner: VitestRunner,
  test: Test,
  hooks: ((context: TestContext) => Awaitable<void>)[],
  sequence: SequenceHooks,
) {
  if (sequence === 'stack') {
    hooks = hooks.slice().reverse()
  }

  if (!hooks.length) {
    return
  }

  const context = test.context as WriteableTestContext

  const onTestFailed = test.context.onTestFailed
  const onTestFinished = test.context.onTestFinished
  context.onTestFailed = () => {
    throw new Error(`Cannot call "onTestFailed" inside a test hook.`)
  }
  context.onTestFinished = () => {
    throw new Error(`Cannot call "onTestFinished" inside a test hook.`)
  }

  if (sequence === 'parallel') {
    try {
      await Promise.all(hooks.map(fn => limitMaxConcurrency(() => fn(test.context))))
    }
    catch (e) {
      failTask(test.result!, e, runner.config._diffOptions)
    }
  }
  else {
    for (const fn of hooks) {
      try {
        await limitMaxConcurrency(() => fn(test.context))
      }

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Register `onTestFailed` inside the test body itself, not inside hooks.
  2. If you need conditional failure handling per test, pass a flag and register the handler in each test.
  3. Use `afterEach` for cleanup that must run regardless, instead of onTestFailed.

Example fix

// before
beforeEach(({ onTestFailed }) => {
  onTestFailed(() => cleanup()) // throws
})
// after
afterEach(() => {
  cleanup()
})
Defensive patterns

Strategy: validation

Validate before calling

// Cannot call onTestFailed from a hook by design; the guard is structural.
// Ensure no beforeEach/afterEach body references onTestFailed.
// Grep your suite: rg 'onTestFailed' inside hook definitions.

Prevention

When it happens

Trigger: Calling `context.onTestFailed(fn)` (or `onTestFailed(fn)` where globals resolve to the context's method) from inside a `beforeEach` or `afterEach` hook body.

Common situations: Sharing setup logic that registers failure handlers; refactoring test helpers into hooks without realizing they call onTestFailed.

Related errors


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