Skip to content
Latchkey

Vitest watch mode hangs the CI job (use vitest run) in CI

Bare vitest (without a subcommand) starts in watch mode. On a CI runner there is no TTY to press keys or exit, so the process runs the suite once then waits forever until the job times out. Use vitest run for a single non-watching pass.

What this error means

Tests appear to pass, then the job stalls and is eventually killed by the workflow timeout. The log shows the watch prompt "Waiting for file changes..." and never returns to the shell.

Vitest
 PASS  src/sum.test.ts
Test Files  1 passed (1)
     Tests  3 passed (3)

 Waiting for file changes...
       press h to show help, press q to quit

Common causes

The test script uses bare vitest

A "test": "vitest" script defaults to watch mode. Interactively that is convenient; in CI it never terminates.

CI was not detected as non-interactive

Vitest exits watch automatically when it detects CI, but a custom shell or unset CI variable can leave watch mode active.

How to fix it

Run the suite once with vitest run

  1. Change the CI command to vitest run (or add a dedicated test:ci script).
  2. Keep vitest (watch) only for local development.
  3. Confirm the process exits with a code instead of waiting.
package.json
{
  "scripts": {
    "test": "vitest",
    "test:ci": "vitest run"
  }
}

Ensure the CI env var is set

GitHub Actions sets CI=true, which makes Vitest default to a single run. If you run in a custom container, export it explicitly.

.github/workflows/ci.yml
env:
  CI: true

How to prevent it

  • Use vitest run for every non-interactive invocation.
  • Keep a separate watch script for local use only.
  • Ensure CI=true is present in custom runner environments.

Frequently asked questions

What causes "Watch mode never exits"?
A "test": "vitest" script defaults to watch mode. Interactively that is convenient; in CI it never terminates.
How do I fix Watch mode never exits?
Run the suite once with vitest run

Related guides

References

Latchkey auto-heals failures like this one - detected, fixed, and retried without you. Start free → 30-day trial · No credit card