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 executing setup/teardown hooks (beforeEach/afterEach/beforeAll/afterAll), Vitest replaces context.onTestFailed with a function that throws this error. onTestFailed only makes sense for the test about to run or that just ran; within a hook there is no single test to attach the failure handler to. The original context.onTestFailed is restored after hooks complete.
Solutions
- Move the onTestFailed registration into the test body where it belongs.
- If you need hook-scoped failure handling, capture failures via try/catch in the hook or use afterEach to inspect test.result.state.
- Make helpers accept a flag so they skip onTestFailed registration when called from hooks.
Example fix
// before
beforeEach(() => {
ctx.onTestFailed(() => log('failed')) // throws
})
// after
test('x', () => {
ctx.onTestFailed(() => log('failed'))
}) Defensive patterns
Strategy: validation
Validate before calling
function safeOnTestFailed(ctx, fn) {
// only call from a test body; detect hook context heuristically
if (!ctx.task?.result || ctx.task.result.state !== 'run') return
ctx.onTestFailed(fn)
} Prevention
- Never call context.onTestFailed from within before*/after* hooks.
- Design helpers to accept an explicit 'inHook' flag to skip registration.
- Keep failure-handler registration inside the test body.
When it happens
Trigger: Calling `context.onTestFailed(fn)` or `this.onTestFailed(fn)` from inside a beforeEach/afterEach/beforeAll/afterAll callback; calling it transitively via a helper invoked from a hook.
Common situations: Shared helpers that register onTestFailed unconditionally; refactoring a test-body assertion into a hook; library code (e.g., custom matchers) that registers failure callbacks.
Related errors
- Cannot call "onTestFinished" inside a test hook.
- Hook () can only be called inside a test
- Cannot annotate tests outside of the test run. The test
- expect.soft() can only be used inside a test
- The ` ` callback was called multiple times in the ` ` hook…
AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11).
Data as JSON: /api/errors/1e7573a3db72fcca.
Report an issue: GitHub.
Appendix: 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 1fa9837ec2)