vitest-dev/vitest · error · Error

Cannot take a screenshot without a test path

Error message

Cannot take a screenshot without a test path

What it means

Vitest's browser screenshot command (takeScreenshot) resolves the output path from the running test file. It requires context.testPath to compute where to save the screenshot. Without a bound test file there is no deterministic location, so the command refuses to run. This is the entry behind page.screenshot(), expect(element).toMatchScreenshot(), and the expect(page).toMatchSnapshot() flow when targeting the browser.

Solutions

  1. Move the screenshot call inside an `it('...', async () => { ... })` body so a testPath is bound.
  2. Verify `test.browser` project is using `pool: 'browser'` and the file matches `test.include` for the browser project, not a node project.
  3. If invoking from a custom browser command, only call it when `context.testPath` is defined; guard with `if (!context.testPath) return`.
  4. Ensure the test file is not imported by a non-browser runner (e.g. eslint, typecheck, or a node test config) that would execute it without testPath.

Example fix

// before
import { page } from 'vitest/browser'
page.screenshot() // at module top-level -> throws

// after
import { test, expect } from 'vitest'
import { page } from 'vitest/browser'
test('renders', async () => {
  await expect(page).toHaveScreenshot()
})
Defensive patterns

Strategy: validation

Validate before calling

import type { BrowserCommandContext } from 'vitest/node'

function assertInTest(ctx: BrowserCommandContext): asserts ctx is BrowserCommandContext & { testPath: string } {
  if (!ctx.testPath) {
    throw new Error('page.screenshot() must be called inside an it()/test() block')
  }
}

// before screenshotting:
assertInTest(context)

Type guard

function hasTestPath(ctx: { testPath?: string }): ctx is { testPath: string } {
  return typeof ctx.testPath === 'string' && ctx.testPath.length > 0
}

Prevention

When it happens

Trigger: Calling page.screenshot() / expect(locator).toMatchScreenshot() from outside an `it`/`test` callback (e.g. module top-level, beforeAll/beforeEach that runs without testPath, a plain script importing vitest/browser, or a custom browser command invoked without a test in scope). Also when a screenshot is triggered during global teardown where BrowserCommandContext.testPath is undefined.

Common situations: Running browser tests with a pool that does not set testPath; invoking screenshot logic from a helper imported by both browser and non-browser specs; misconfigured project where browser tests accidentally run under the `forks`/`threads` pool; calling takeScreenshot via a registered browser command at a time vitest has not yet entered a test.

Related errors


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

Appendix: source

Thrown at packages/browser-playwright/src/commands/screenshot.ts:38

    transform: none !important;
  }
`

/**
 * Takes a screenshot using the provided browser context and returns a buffer and the expected screenshot path.
 *
 * **Note**: the returned `path` indicates where the screenshot *might* be found.
 * It is not guaranteed to exist, especially if `options.save` is `false`.
 *
 * @throws {Error} If the function is not called within a test or if the browser provider does not support screenshots.
 */
export async function takeScreenshot(
  context: BrowserCommandContext,
  name: string,
  options: Omit<ScreenshotCommandOptions, 'base64'>,
): Promise<{ buffer: Buffer<ArrayBufferLike>; path: string }> {
  if (!context.testPath) {
    throw new Error(`Cannot take a screenshot without a test path`)
  }

  const path = resolveScreenshotPath(
    context.testPath,
    name,
    context.project.config,
    options.path,
  )

  // playwright does not need a screenshot path if we don't intend to save it
  let savePath: string | undefined

  if (options.save) {
    savePath = normalize(path)

    assertBrowserApiWrite(context.project, savePath)
    assertBrowserFileAccess(context.project, savePath)

View on GitHub (pinned to 1fa9837ec2)