{"id":"797c705a520cb0f0","repo":"vitest-dev/vitest","slug":"cannot-annotate-tests-outside-of-the-test-run-the","errorCode":null,"errorMessage":"Cannot annotate tests outside of the test run. The test \"${test.name}\" finished running with the \"${test.result.state}\" state already.","messagePattern":"Cannot annotate tests outside of the test run\\. The test \"(.+?)\" finished running with the \"(.+?)\" state already\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/runtime/runner/context.ts","lineNumber":192,"sourceCode":"  context.task = test\n\n  context.skip = (condition?: boolean | string, note?: string): never => {\n    if (condition === false) {\n      // do nothing\n      return undefined as never\n    }\n    test.result ??= { state: 'skip' }\n    test.result.pending = true\n    throw new PendingError(\n      'test is skipped; abort execution',\n      test,\n      typeof condition === 'string' ? condition : note,\n    )\n  }\n\n  context.annotate = ((message, type, attachment) => {\n    if (test.result && test.result.state !== 'run') {\n      throw new Error(`Cannot annotate tests outside of the test run. The test \"${test.name}\" finished running with the \"${test.result.state}\" state already.`)\n    }\n\n    const annotation: TestAnnotation = {\n      message,\n      type: typeof type === 'object' || type === undefined ? 'notice' : type,\n    }\n    const annotationAttachment = typeof type === 'object' ? type : attachment\n\n    if (annotationAttachment) {\n      annotation.attachment = annotationAttachment\n\n      manageArtifactAttachment(annotation.attachment)\n    }\n\n    return recordAsyncOperation(\n      test,\n      recordArtifact(test, { type: 'internal:annotation', annotation }).then(async ({ annotation }) => {\n        if (!runner.onTestAnnotate) {","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/vitest/src/runtime/runner/context.ts#L174-L210","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure `annotate()` is called synchronously within the test body or within a properly awaited async flow before the test returns.","Move annotation logic that depends on deferred results into `onTestFinished`/`onTestFailed` handlers registered during the test.","Await any promise that calls `annotate` before letting the test function return.","If annotating from a hook, use the hook's own context rather than a stale captured test context."],"exampleFix":"// before\ntest('x', ({ annotate }) => {\n  setTimeout(() => annotate('late note'), 100) // test finishes first\n})\n// after\ntest('x', async ({ annotate }) => {\n  await new Promise(r => setTimeout(r, 100))\n  annotate('timely note')\n})","handlingStrategy":"validation","validationCode":"function canAnnotate(test: { result?: { state?: string } }): boolean {\n  return !test.result || test.result.state === 'run'\n}\n// usage inside a deferred callback:\nif (canAnnotate(context.task)) {\n  context.annotate('note')\n}","typeGuard":"function testIsRunning(test: { result?: { state?: string } }): test is { result: { state: 'run' } } {\n  return test.result?.state === 'run'\n}","tryCatchPattern":"try {\n  context.annotate('maybe-late')\n} catch (e) {\n  if (e instanceof Error && /Cannot annotate tests outside/.test(e.message)) {\n    // test already finished; annotation is moot, swallow or log\n  } else throw e\n}","preventionTips":["Always await any promise that calls annotate before the test returns.","Prefer annotating synchronously at the point the information is known.","Avoid fire-and-forget timers that call annotate."],"tags":["annotation","lifecycle","async","test-context"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}