vitest-dev / vitest-dev/vitest

vmThreads + isolate:false + deps.optimizer: vi.mock misses and duplicate module identity — green on 4.1.11, flaky every run on 5.0.1

Open
#11,289 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
17.1k
Forks
2k
Avg merge
1d 22h
Merged PRs (30d)
94

Description

Describe the bug

On a large monorepo suite (~300 files, ~3900 tests) that runs pool: 'vmThreads' with deps.optimizer enabled, upgrading 4.1.11 → 5.0.1 turns a deterministically green suite into one that fails a different set of tests on every run. The same config on 4.1.11 is green 3/3.

Two distinct symptom shapes, both pointing at module identity rather than test logic:

  1. vi.mock silently does not apply — the real module is used instead, with no error:
    AssertionError: expected "vi.fn()" to be called 1 times, but got 0 times
    
  2. Duplicate module instances — the same class reached through two different module instances:
    AssertionError: expected AppError: Etwas ist schiefgelaufen {...} to be an instance of AppError
    

Both are confined to the vmThreads project (isolate: false, which vm pools force). A sibling project on pool: 'forks' with isolation is green on both versions. Our backend integration suite shows the same signature: ~30 files fail, all in the isolate: false project, with membership shifting between runs.

A/B, identical config, same machine

Frontend workspace, both projects, 3 consecutive runs each:

vitest result
4.1.11 3/3 green — 299 files, 3927 tests passed
5.0.1 fails every run — 2 to 18 tests, different set each time

This is the part I would most like to flag: #11284 reports a closely related vmThreads cross-file vi.mock leak and states it is not a v5 regression ("observed on both 4.0.18 and 5.0.1"). For the isolate: false case in this repo that does not hold — 4.1.11 is deterministically green across repeated runs, 5.0.1 is not.

The optimizer / shared cache dir dimension

This is the part I could not find covered in any existing issue.

Two project entries each enable deps.optimizer (client and ssr) over a set of first-party workspace packages, and a small plugin forces both projects' cacheDir to one shared directory (so the prebundle is built once and reused by both).

Failure counts scale with that sharing:

configuration failures per run
parallel-env project alone (forks, isolated) 0
parallel project alone (vmThreads) 2–3, shifting
both projects together, one shared cacheDir 15–18, shifting
both projects, per-project cacheDir 2–7, shifting
both projects, deps.optimizer disabled entirely 0

Disabling deps.optimizer is the only configuration that makes the suite fully green on v5. Cold cache also helps markedly: the first run after deleting node_modules/.vite produced 1 failure, the next run with the now-warm prebundle produced 15.

Stack traces on failing assertions point into prebundled chunks carrying ?v=<hash> query strings, e.g.:

❯ ErrorResponse node_modules/.vite/vitest/first-party/deps_ssr/<pkg>.js?v=124a2a15:1129:25

which is the same module-identity mechanism described in #11193. That one was fixed by #11199 in 5.0.1 and did improve things a lot here (5.0.0 was far worse — 15–19 failures per run even for a single project), but it did not eliminate the problem.

Reproduction

I do not have a minimal reproduction, and I want to be upfront about that rather than file a synthetic one that does not actually reproduce.

I built a scratch project mirroring the config shape — two projects (vmThreads + forks) sharing one optimizer cache dir, a local workspace package in deps.optimizer.include, one file mocking that package, a sibling file asserting instanceof and the real implementation through a shared transitive consumer — and scaled it to 63 files / 124 tests. It stays green across 3 runs on 5.0.1. This matches the experience reported in #11284, where plain multi-file setups did not reproduce either and the trigger needed @vitejs/plugin-react + .tsx + vi.hoisted.

Differences between my scratch project and the real suite that I have not yet isolated: @vitejs/plugin-react and .tsx test files, per-file @vitest-environment jsdom/happy-dom pragmas, a much larger first-party module graph, and custom rolldownOptions (a output.banner cache-buster keyed on first-party source dirs, plus plugins that externalise node builtins and drop sourcemaps).

I am very happy to bisect, run patched builds, or instrument a specific code path against the real suite — it reproduces reliably there within a single run, so turnaround is fast. If it would help, I can also try to carve a shareable reproduction out of the real repo.

Related
  • #11152 — under isolate: false a file receives another file's vi.mock through a shared transitive import. Same project configuration as ours. A maintainer notes one direction is intended (the consumer module is already executed and cached).
  • #11284 — vmThreads cross-file vi.mock leak. Same symptom class; the A/B above is offered as a counterpoint to the "not a v5 regression" assessment.
  • #11193 / #11199 — the ?v=<browserHash> cache staleness that made vi.mock miss; fixed in 5.0.1 and a large improvement here, but not a complete fix.

If maintainers consider this a duplicate of #11152 or #11284, please close it and I will move the A/B data and the optimizer/cacheDir findings into whichever thread is most useful.

Config shape
const optimizer = { enabled: true, include: [/* first-party workspace pkgs */], exclude: [...],
  rolldownOptions: { platform: 'node', plugins: [/* externalise builtins, drop sourcemaps */],
    output: /* banner cache-buster keyed on first-party source dirs */ } };

export default defineConfig({
  plugins: [sharedPrebundleCacheDirPlugin(__dirname)], // forces both projects to ONE cacheDir
  test: {
    maxWorkers: 4,
    vmMemoryLimit: '512MB',
    deps: { optimizer: { client: { enabled: false }, ssr: { enabled: false } } }, // off at root
    projects: [
      { extends: true, test: { name: 'parallel', pool: 'vmThreads', maxWorkers: 4,
          deps: { optimizer: { client: optimizer, ssr: optimizer } } } },
      { extends: true, test: { name: 'parallel-env', pool: 'forks', maxWorkers: 4,
          deps: { optimizer: { client: optimizer, ssr: optimizer } } } },
    ],
  },
});
System Info
System:
    OS: macOS 26.6.2
    CPU: (12) arm64 Apple M2 Max
    Memory: 9.86 GB / 64.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 24.21.0
    npm: 11.19.0
    bun: 1.4.2
  npmPackages:
    @vitest/coverage-v8: 5.0.1 => 5.0.1
    vitest: 5.0.1 => 5.0.1
    vite: 8.2.2 => 8.2.2
Used Package Manager

bun

Validations

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Begin with the provided two-project configuration, focusing on the vmThreads project, isolate:false, deps.optimizer, and the shared cacheDir plugin. Re-run the frontend suite with optimizer enabled, per-project cache directories, a shared cache directory, and optimizer disabled, then compare the shifting vi.mock and instanceof failures. Done means identifying the remaining module-identity cause or producing a minimal reproduction that isolates it.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript, vite
Domain
build-system, devtools, testing-qa
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.