Skip to content

DevTools opens a fixed-port (7812) WS server under Vitest, parallel monorepo test runs crash with EADDRINUSE #517

Description

@mymx2

What happens

With DevTools() in a shared Vite config, running Vitest in multiple workspace packages in parallel (e.g. vp test -r / vitest -r) crashes all but one process:

Error: listen EADDRINUSE: address already in use ::1:7812

Single-package runs pass but still print: Tests closed successfully but something prevents Vite server from exiting — the DevTools WS server stays alive after the test run.

Root cause

  1. DevToolsServer uses apply: 'serve' and starts the middleware in configureServer (packages/core/src/node/plugins/server.ts:44). Vitest runs Vite with command: 'serve', so the hook fires during test runs.
  2. Under Vitest there is no listening Vite HTTP server, so viteDevServer.httpServer is nullish → routeBound is false (packages/core/src/node/ws.ts:72-73) → the standalone path opens a dedicated WS port.
  3. That port defaults to getPort({ port: 7812, random: true }) (packages/core/src/node/ws.ts:74). get-port-please probes 7812 first and returns it when free; the random fallback only kicks in when the probe fails. Parallel test processes probe before any of them binds (TOCTOU), all "win" 7812, and all but one crash on the real bind.

So a plugin intended for dev servers activates in the test pipeline and binds a well-known fixed port per process.

Repro

  1. Monorepo with two or more packages whose vite.config includes DevTools().
  2. Run Vitest in all packages in parallel (e.g. vitest run via a recursive task runner).
  3. All but one process crash with EADDRINUSE on ::1:7812.

Expected

One of:

  • DevTools does not activate when there is no real dev server (e.g. guard on config.server.middlewareMode and/or the VITEST env var), or
  • the standalone WS port allocation is race-safe for parallel processes (bind port 0 instead of probe-then-bind on a fixed default), or
  • a documented option to disable the WS server / set its port.

Workaround

Conditionally load the plugin outside test runs:

plugins: [...(process.env.VITEST === undefined ? [DevTools()] : []), /* ... */]

Verified: with this guard, parallel test runs across 3 packages pass reliably; without it they crash deterministically.

Environment

  • @vitejs/devtools 0.4.12
  • vitest 4.1.10 (via vite-plus 0.2.7)
  • Node.js 24.19.0, Windows 11

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions