Describe the bug
In browser mode, vitest list (collect-only) imports several test files through one browser page
when file parallelism doesn't give each file its own page. The mocker registry is scoped to the
page, while each vi.mock call is written to bind its own file — so during collection, one file's
mock is visible to (or missing for) the files that share its page. vitest run isolates the same
files per page and passes.
Two error shapes, same cause:
- A factory mock that replaces the module: a page-mate importing any export the factory doesn't
provide fails with SyntaxError: The requested module '…' does not provide an export named '…'.
- A factory mock that spreads
importOriginal: the page-mate fails with Error: Mock …/m.ts wasn't registered. This is probably a Vitest error. Please, open a new issue with reproduction.
(ModuleMocker.resolveFactoryModule is asked for an id the page's registry no longer holds).
--no-file-parallelism makes it deterministic (below). Without it, the same leak still fires
whenever the worker budget packs several files into one page — in our monorepo (11 projects sharing
maxWorkers: '50%'), a bare vitest list at the root fails 3 runs out of 3 with rotating victim
files, which is what makes this nasty: the errors read like a broken tree, not a broken listing.
Related issues
Disclosure
Human posted. Prepared with Claude Fable.
Reproduction
package.json deps: vitest@4.1.10, @vitest/browser@4.1.10, @vitest/browser-playwright@4.1.10,
playwright@1.61.1.
// vitest.config.ts
import { playwright } from '@vitest/browser-playwright';
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
browser: {
enabled: true,
provider: playwright(),
headless: true,
instances: [{ browser: 'chromium' }],
},
},
});
// test/m.ts
export const a = 'real-a';
export const b = 'real-b';
// test/mocker.test.ts
import { expect, test, vi } from 'vitest';
vi.mock('./m', () => ({ a: 'mocked-a' }));
import { a } from './m';
test('sees the mock', () => {
expect(a).toBe('mocked-a');
});
// test/victim.test.ts
import { expect, test } from 'vitest';
import { b } from './m';
test('sees the real module', () => {
expect(b).toBe('real-b');
});
$ npx vitest run --no-file-parallelism # 2 passed — run isolates the files
$ npx vitest list # exit 0 — each file got its own page
$ npx vitest list --no-file-parallelism # exit 1:
Error: Failed to import test file …/test/victim.test.ts
Caused by: SyntaxError: The requested module '/test/m.ts' does not provide an export named 'b'
Changing the factory to async (importOriginal) => ({ ...(await importOriginal()), a: 'mocked-a' })
switches the failure to shape 2:
$ npx vitest list --no-file-parallelism # exit 1:
Error: Failed to import test file …/test/victim.test.ts
Caused by: Error: Mock /test/m.ts wasn't registered. This is probably a Vitest error. …
Expected behavior
Collection sees the same per-file mock scoping a run does — either by isolating collect-only imports
per file, or by scoping the mocker registry to the importing file rather than the page.
System Info
System:
OS: macOS 26.4
CPU: (6) arm64 Apple M1 Max (Virtual)
Memory: 1.94 GB / 10.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 26.7.0 - ~/.vite-plus/js_runtime/node/26.7.0/bin/node
Yarn: 1.22.22 - ~/.vite-plus/bin/yarn
npm: 11.19.0 - ~/.vite-plus/js_runtime/node/26.7.0/bin/npm
pnpm: 11.20.0 - ~/.vite-plus/bin/pnpm
npmPackages:
@vitest/browser: ^4.1.10 => 4.1.10
@vitest/browser-playwright: ^4.1.10 => 4.1.10
playwright: ^1.61.1 => 1.61.1
vitest: ^4.1.10 => 4.1.10
Chromium via playwright, headless.
Used Package Manager
yarn
Validations
Describe the bug
In browser mode,
vitest list(collect-only) imports several test files through one browser pagewhen file parallelism doesn't give each file its own page. The mocker registry is scoped to the
page, while each
vi.mockcall is written to bind its own file — so during collection, one file'smock is visible to (or missing for) the files that share its page.
vitest runisolates the samefiles per page and passes.
Two error shapes, same cause:
provide fails with
SyntaxError: The requested module '…' does not provide an export named '…'.importOriginal: the page-mate fails withError: Mock …/m.ts wasn't registered. This is probably a Vitest error. Please, open a new issue with reproduction.(
ModuleMocker.resolveFactoryModuleis asked for an id the page's registry no longer holds).--no-file-parallelismmakes it deterministic (below). Without it, the same leak still fireswhenever the worker budget packs several files into one page — in our monorepo (11 projects sharing
maxWorkers: '50%'), a barevitest listat the root fails 3 runs out of 3 with rotating victimfiles, which is what makes this nasty: the errors read like a broken tree, not a broken listing.
Related issues
mocked/unmocked files. The workaround suggested there,
fileParallelism: false, is what makesthis collect-only case fail deterministically: in
listmode it puts every file in one page.different trigger.
vitest listbroken in browser mode #7065 (closed) —vitest listin browser mode crashing in the reporter; fixed, unrelatedmechanism, mentioned only because it is the previous "list is broken in browser mode" issue.
Disclosure
Human posted. Prepared with Claude Fable.
Reproduction
package.jsondeps:vitest@4.1.10,@vitest/browser@4.1.10,@vitest/browser-playwright@4.1.10,playwright@1.61.1.Changing the factory to
async (importOriginal) => ({ ...(await importOriginal()), a: 'mocked-a' })switches the failure to shape 2:
Expected behavior
Collection sees the same per-file mock scoping a run does — either by isolating collect-only imports
per file, or by scoping the mocker registry to the importing file rather than the page.
System Info
System: OS: macOS 26.4 CPU: (6) arm64 Apple M1 Max (Virtual) Memory: 1.94 GB / 10.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 26.7.0 - ~/.vite-plus/js_runtime/node/26.7.0/bin/node Yarn: 1.22.22 - ~/.vite-plus/bin/yarn npm: 11.19.0 - ~/.vite-plus/js_runtime/node/26.7.0/bin/npm pnpm: 11.20.0 - ~/.vite-plus/bin/pnpm npmPackages: @vitest/browser: ^4.1.10 => 4.1.10 @vitest/browser-playwright: ^4.1.10 => 4.1.10 playwright: ^1.61.1 => 1.61.1 vitest: ^4.1.10 => 4.1.10 Chromium via playwright, headless.Used Package Manager
yarn
Validations