vitest-dev/vitest · error · Error

Cannot take a screenshot outside of a test.

Error message

Cannot take a screenshot outside of a test.

What it means

page.screenshot() reads the current test from the worker state; if there is no current test (because screenshot is invoked outside the test function body, e.g. at module top-level, in a beforeEach/beforeAll hook, or after the test has finished), Vitest throws. Screenshots are bound to a task for naming and attachment, so they require an active test context.

Solutions

  1. Move the page.screenshot() call inside the body of an it/test function.
  2. If you need a screenshot during setup, capture the DOM state with prettyDOM() instead, or restructure so the screenshot runs inside a test.
  3. Ensure no setTimeout/setInterval or detached promise triggers the screenshot after the test ends.

Example fix

// before
beforeAll(async () => { await page.screenshot() })
// after
it('renders', async () => { await page.screenshot() })
Defensive patterns

Strategy: validation

Validate before calling

import { getWorkerState } from 'vitest'
if (!getWorkerState().current) {
  // skip screenshot — not inside a test
} else {
  await page.screenshot()
}

Type guard

function insideTest(): boolean {
  return !!getWorkerState().current
}

Prevention

When it happens

Trigger: Calling page.screenshot() at the top level of a spec file; calling it inside beforeAll/beforeEach/afterEach/afterAll; calling it after an await that outlives the test (e.g. setTimeout callback); calling it during module import or in a helper invoked outside a test.

Common situations: Taking screenshots for debugging in a setup hook; refactoring that moved the screenshot call out of the test body; race conditions where an async callback fires after the test completed; helper utilities that assume they are always called from a test.

Related errors


AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11). Data as JSON: /api/errors/c141002116b3d322. Report an issue: GitHub.

Appendix: source

Thrown at packages/browser/src/client/tester/context.ts:315

      iframeId: id,
    } satisfies IframeViewportEvent)
    return new Promise((resolve, reject) => {
      channel.addEventListener('message', function handler(e) {
        if (e.data.event === 'viewport:done' && e.data.iframeId === id) {
          channel.removeEventListener('message', handler)
          resolve()
        }
        if (e.data.event === 'viewport:fail' && e.data.iframeId === id) {
          channel.removeEventListener('message', handler)
          reject(new Error(e.data.error))
        }
      })
    })
  },
  async screenshot(options = {}) {
    const currentTest = getWorkerState().current
    if (!currentTest) {
      throw new Error('Cannot take a screenshot outside of a test.')
    }

    if (currentTest.concurrent) {
      throw new Error(
        'Cannot take a screenshot in a concurrent test because '
        + 'concurrent tests run at the same time in the same iframe and affect each other\'s environment. '
        + 'Use a non-concurrent test to take a screenshot.',
      )
    }

    const repeatCount = currentTest.result?.repeatCount ?? 0
    const taskName = getTaskFullName(currentTest)
    const number = screenshotIds[repeatCount]?.[taskName] ?? 1

    screenshotIds[repeatCount] ??= {}
    screenshotIds[repeatCount][taskName] = number + 1

    const name

View on GitHub (pinned to 1fa9837ec2)