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.
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 quitCommon 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
- Change the CI command to
vitest run(or add a dedicatedtest:ciscript). - Keep
vitest(watch) only for local development. - Confirm the process exits with a code instead of waiting.
{
"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.
env:
CI: trueHow to prevent it
- Use
vitest runfor every non-interactive invocation. - Keep a separate watch script for local use only.
- Ensure
CI=trueis present in custom runner environments.