vitest-dev/vitest · error · Error
Cannot call "onTestFinished" inside a test hook.
Error message
Cannot call "onTestFinished" inside a test hook.
What it means
Symmetric to error 347, during hook execution Vitest replaces `context.onTestFinished` with a throwing stub (run.ts:141-143). Registering an onTestFinished handler from within `beforeEach`/`afterEach` is disallowed because the registration window for that test has effectively closed.
Source
Thrown at packages/vitest/src/runtime/runner/run.ts:142
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))
}
catch (e) {
failTask(test.result!, e, runner.config._diffOptions)
}View on GitHub (pinned to d568f8ce37)
Solutions
- Register `onTestFinished` inside the test body, not in hooks.
- Use `afterEach` for guaranteed post-test cleanup instead of onTestFinished.
- Restructure shared setup helpers to return cleanup callbacks the caller invokes.
Example fix
// before
beforeEach(({ onTestFinished }) => {
const db = connect()
onTestFinished(() => db.close()) // throws
})
// after
afterEach(() => {
closeDbIfOpen()
}) Defensive patterns
Strategy: validation
Validate before calling
// Ensure no beforeEach/afterEach body references onTestFinished. // Grep: rg 'onTestFinished' within hook callbacks; move logic to afterEach.
Prevention
- Never reference onTestFinished inside beforeEach/afterEach.
- Use afterEach for guaranteed teardown.
- Register onTestFinished inside the test body only.
When it happens
Trigger: Calling `context.onTestFinished(fn)` or the global `onTestFinished(fn)` from inside a `beforeEach`/`afterEach` hook.
Common situations: Porting onTestFinished-based cleanup into a shared hook; helpers that wrap both setup and teardown registration.
Related errors
- Hook ${name}() can only be called inside a test
- Cannot call "onTestFailed" inside a test hook.
- Cannot take a screenshot outside of a test.
- 'toMatchScreenshot' cannot be used without test context
- Cannot annotate tests outside of the test run. The test "${t
AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03).
Data as JSON: /data/errors/e697a1e872c975a5.json.
Report an issue: GitHub.