vitest-dev/vitest · error · Error

Cannot annotate tests outside of the test run. The test "${t

Error message

Cannot annotate tests outside of the test run. The test "${test.name}" finished running with the "${test.result.state}" state already.

What it means

The `context.annotate()` method attaches metadata annotations to a test, but it can only be called while the test is actively running (state `'run'`). If the test has already settled into a final state (`pass`, `fail`, `skip`), calling `annotate` throws this error (context.ts:190-192). This prevents annotating tests from deferred/async code that outlives the test's execution window.

Source

Thrown at packages/vitest/src/runtime/runner/context.ts:192

  context.task = test

  context.skip = (condition?: boolean | string, note?: string): never => {
    if (condition === false) {
      // do nothing
      return undefined as never
    }
    test.result ??= { state: 'skip' }
    test.result.pending = true
    throw new PendingError(
      'test is skipped; abort execution',
      test,
      typeof condition === 'string' ? condition : note,
    )
  }

  context.annotate = ((message, type, attachment) => {
    if (test.result && test.result.state !== 'run') {
      throw new Error(`Cannot annotate tests outside of the test run. The test "${test.name}" finished running with the "${test.result.state}" state already.`)
    }

    const annotation: TestAnnotation = {
      message,
      type: typeof type === 'object' || type === undefined ? 'notice' : type,
    }
    const annotationAttachment = typeof type === 'object' ? type : attachment

    if (annotationAttachment) {
      annotation.attachment = annotationAttachment

      manageArtifactAttachment(annotation.attachment)
    }

    return recordAsyncOperation(
      test,
      recordArtifact(test, { type: 'internal:annotation', annotation }).then(async ({ annotation }) => {
        if (!runner.onTestAnnotate) {

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Ensure `annotate()` is called synchronously within the test body or within a properly awaited async flow before the test returns.
  2. Move annotation logic that depends on deferred results into `onTestFinished`/`onTestFailed` handlers registered during the test.
  3. Await any promise that calls `annotate` before letting the test function return.
  4. If annotating from a hook, use the hook's own context rather than a stale captured test context.

Example fix

// before
test('x', ({ annotate }) => {
  setTimeout(() => annotate('late note'), 100) // test finishes first
})
// after
test('x', async ({ annotate }) => {
  await new Promise(r => setTimeout(r, 100))
  annotate('timely note')
})
Defensive patterns

Strategy: validation

Validate before calling

function canAnnotate(test: { result?: { state?: string } }): boolean {
  return !test.result || test.result.state === 'run'
}
// usage inside a deferred callback:
if (canAnnotate(context.task)) {
  context.annotate('note')
}

Type guard

function testIsRunning(test: { result?: { state?: string } }): test is { result: { state: 'run' } } {
  return test.result?.state === 'run'
}

Try / catch

try {
  context.annotate('maybe-late')
} catch (e) {
  if (e instanceof Error && /Cannot annotate tests outside/.test(e.message)) {
    // test already finished; annotation is moot, swallow or log
  } else throw e
}

Prevention

When it happens

Trigger: Calling `context.annotate(...)` inside a `setTimeout`/`setInterval`/microtask that fires after the test resolved; annotating inside `onTestFailed`/`onTestFinished` hooks on a context whose test already finished; awaiting a fire-and-forget promise that calls annotate after the test ends.

Common situations: Fire-and-forget async operations that outlive the test; annotation in cleanup hooks; race conditions where the test completes before a background annotation call resolves.

Related errors


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