vitest-dev/vitest · error · Error

vitest/browser can be imported only inside the Browser…

Error message

vitest/browser can be imported only inside the Browser Mode. Your test is running in ${pool} pool. Make sure your regular tests are excluded from the "test.include" glob pattern.

What it means

`vitest/browser` is a virtual module resolved only when the test runs under the browser pool (the tester sets globalThis.__vitest_worker__.ctx.pool = 'browser'). The fallback context.js throws on import; if a pool is present but is not 'browser', the message names the offending pool. This prevents accidentally using browser-only APIs (page, userEvent, cdp) in node-run tests.

Solutions

  1. Narrow the node project's `include` / the browser project's `include` so the same file is not in both.
  2. Make the `vitest/browser` import lazy/conditional (dynamic import inside a function only called from browser tests).
  3. Exclude browser specs from the node project's `test.include` (and vice versa) using separate project configs.
  4. If a third-party dep imports it unconditionally, report upstream or alias the module for non-browser projects.

Example fix

// before - vitest.config.ts single project catches everything
export default defineConfig({ test: { include: ['**/*.test.ts'] } })

// after - split projects by environment
export default defineConfig({
  test: {
    workspace: [
      { test: { name: 'unit', include: ['test/unit/**/*.test.ts'] } },
      { test: { name: 'browser', include: ['test/browser/**/*.test.ts'], browser: { provider: 'playwright' } } },
    ],
  },
})
Defensive patterns

Strategy: validation

Validate before calling

// split workspace projects so the same file is not in both node and browser pools
export default defineConfig({
  test: {
    workspace: [
      { test: { name: 'unit', include: ['test/unit/**/*.test.ts'] } },
      { test: { name: 'browser', include: ['test/browser/**/*.test.ts'], browser: { provider: 'playwright' } } },
    ],
  },
})

Type guard

// runtime guard inside shared code
function isBrowserPool(): boolean {
  return globalThis.__vitest_worker__?.ctx?.pool === 'browser'
}

if (isBrowserPool()) {
  // safe to use vitest/browser APIs here
}

Prevention

When it happens

Trigger: A file imports `vitest/browser` (directly or transitively via a component library or shared util) and runs under `forks`, `threads`, `vmThreads`, or `typescript` pool. Typically because the file is matched by both a node test project and the browser project's include globs.

Common situations: Shared util/component imported by both node and browser specs; include globs too broad; a dependency unconditionally imports `vitest/browser`; typecheck pool trying to evaluate the module.

Related errors


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

Appendix: 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 1fa9837ec2)