{"record":{"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/1fa9837ec26533512fdcad8baebf249771bd340a/packages/vitest/src/runtime/runner/context.ts#L174-L210","documentation":"Thrown by context.annotate when the owning test already has a result with a non-'run' state, meaning the test has finished (passed/failed/skipped). Annotations are metadata attached during the live test run; annotating after completion has nowhere to attach. The check reads `test.result.state` and rejects anything other than 'run' or no result.","triggerScenarios":"Calling ctx.annotate(...) inside an onTestFailed or onTestFinished handler (which fire after the result state is set); calling annotate inside a microtask that resolves after the test body returns; calling annotate in a beforeEach hook for a test that was already skipped.","commonSituations":"Moving diagnostic code into afterEach/onTestFailed and forgetting annotate is body-only; awaiting a long promise that crosses the test-finish boundary; annotating from a background timer.","solutions":["Move the annotate call inside the test body, before it returns.","If annotating on failure, attach the diagnostic to the error itself (via expect.assertions or a custom assertion) rather than calling annotate from onTestFailed.","Guard the call: check that the test is still running before annotating."],"exampleFix":"// before\ntest('x', () => {\n  startBackgroundWork()\n})\nonTestFailed(() => {\n  ctx.annotate('late diagnostic') // throws\n})\n\n// after\ntest('x', async () => {\n  const result = await startBackgroundWork()\n  ctx.annotate('diagnostic', { body: result })\n})","handlingStrategy":"validation","validationCode":"function safeAnnotate(ctx, msg, att) {\n  const t = ctx.task\n  if (t.result && t.result.state !== 'run') return\n  return ctx.annotate(msg, att)\n}","typeGuard":"const testIsRunning = (t: Test) =>\n  !t.result || t.result.state === 'run'","tryCatchPattern":"try {\n  await ctx.annotate(msg)\n} catch (e) {\n  if (/Cannot annotate tests outside/.test(e.message)) return // swallow: too late\n  throw e\n}","preventionTips":["Call annotate only inside the test body.","Do not annotate from onTestFailed/onTestFinished or background tasks.","Await all work before the test body returns."],"tags":["test-context","annotations","lifecycle","artifact"],"backgroundTag":null,"analyzedSha":"1fa9837ec26533512fdcad8baebf249771bd340a","analyzedAt":"2026-08-11T16:11:39.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}