vitest-dev/vitest · error · Error

vitest/browser can be imported only inside the Browser Mode.

Error message

vitest/browser can be imported only inside the Browser Mode. ${pool ? `Your test is running in ${pool} pool. Make sure your regular tests are excluded from the "test.include" glob pattern.` : 'Instead, it was imported outside of Vitest.'}

What it means

Thrown at module load time by the fallback vitest/browser module (packages/browser/context.js). The real vitest/browser is a virtual module injected only when a test runs in Browser Mode (browser pool). This stub is what Node resolves when something imports vitest/browser from a normal (non-browser) Vitest run, and it throws immediately with a hint about the active pool.

Source

Thrown at packages/browser/context.js:14

// Vitest resolves "vitest/browser" as a virtual module instead

// fake exports for static analysis
export const page = null
export const server = null
export const userEvent = null
export const cdp = null
export const commands = null
export const locators = null
export const utils = null

const pool = globalThis.__vitest_worker__?.ctx?.pool

throw new Error(
  // eslint-disable-next-line prefer-template
  'vitest/browser can be imported only inside the Browser Mode. '
  + (pool
    ? `Your test is running in ${pool} pool. Make sure your regular tests are excluded from the "test.include" glob pattern.`
    : 'Instead, it was imported outside of Vitest.'),
)

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Run the importing tests in browser mode: vitest --browser (or set projects with browser.enabled).
  2. Exclude browser tests from the regular suite: set test.include so browser files are not picked up by the default pool run, or split projects.
  3. Split shared utils so the @vitest/browser import lives only in browser-only files.
  4. Confirm the active pool is 'browser' for the failing file.

Example fix

// before: shared util imported by both node and browser tests
// src/shared.ts
import { page } from '@vitest/browser' // explodes under node pool
export const click = () => page.click('#x')

// after: vitest.config.ts splits projects
export default defineConfig({
  test: {
    projects: [
      { test: { name: 'unit', include: ['test/unit/**/*.test.ts'] } },
      { test: { name: 'browser', include: ['test/browser/**/*.test.ts'], browser: { enabled: true, provider: 'playwright' } } },
    ],
  },
})
Defensive patterns

Strategy: validation

Validate before calling

const pool = globalThis.__vitest_worker__?.ctx?.pool
if (pool !== 'browser') {
  throw new Error(`vitest/browser requires the browser pool; active pool: ${pool}`)
}
// then import vitest/browser dynamically inside the test

Type guard

function inBrowserPool(): boolean {
  return globalThis.__vitest_worker__?.ctx?.pool === 'browser'
}

Prevention

When it happens

Trigger: Importing page, userEvent, server, cdp, etc. from 'vitest/browser' (directly or transitively via a shared util) in a test that runs under the forks/threads/vmThreads/vmForks pool instead of the browser pool. The message names the offending pool when one is set.

Common situations: A shared helper that imports @vitest/browser is also imported by non-browser unit tests; browser tests not excluded from the default test.include so they run under the regular pool; config that failed to set browser.enabled / browser.provider; CI running the full suite without the browser flag.

Related errors


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