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

Thrown by the Playwright screenshot browser command (vitest/browser page.screenshot / expect screenshot internals) when context.testPath is missing. Vitest derives the screenshot output path from the running test's file, so a screenshot must be taken synchronously inside a test/task that has a registered test path.

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 d568f8ce37)

Solutions

  1. Move the screenshot call inside an it/test block so Vitest attaches a testPath.
  2. If using a custom browser command, only invoke it from within a running test.
  3. For setup-time captures, use Playwright's page.screenshot directly with an explicit path instead of Vitest's screenshot API.

Example fix

// before (setup.ts, imported globally)
import { page } from '@vitest/browser'
await page.screenshot() // no test context -> throws

// after (inside a test file)
import { test } from 'vitest'
import { page } from '@vitest/browser'
test('visual', async () => {
  await page.screenshot()
})
Defensive patterns

Strategy: validation

Validate before calling

if (!context.testPath) {
  throw new Error('page.screenshot() must be called from within a test')
}
await takeScreenshot(context, name, options)

Type guard

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

Try / catch

try {
  await takeScreenshot(ctx, name, opts)
} catch (err) {
  if (err instanceof Error && /without a test path/.test(err.message)) {
    // not in a test: skip or route to a direct playwright screenshot
  } else throw err
}

Prevention

When it happens

Trigger: Calling the screenshot API from global setup, a beforeAll/beforeEach in a non-test module, a worker thread, or any context where Vitest has not associated the call with a test file. Also triggered by invoking the command manually with a synthesized BrowserCommandContext lacking testPath.

Common situations: Taking screenshots in setup files or shared utils imported outside tests, running screenshot logic in onConsoleLog/onTask hooks, or calling page.screenshot inside a custom command registered outside the test lifecycle.

Related errors


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