Skip to content

Vitest run stalls indefinitely mid-suite after a workerd pool test file finishes, with no error output #15498

Description

@pbomb

Summary

A Vitest run using @cloudflare/vitest-pool-workers can hang indefinitely partway through a multi-file run: a workerd-backed test file's assertions and console.* output all print successfully, and then the run produces zero further output — no next test file starting, no completion summary, no error, no stack trace — until an external watchdog/timeout kills the process. This looks distinct from #15085 (fixed in 0.22.0) and #14180 (fixed): those hangs happen at final process teardown; this one happens mid-run, before the suite has even finished running all test files.

Versions

Observed behavior

  1. A Vitest run includes several test files; at least one exercises a Worker via @cloudflare/vitest-pool-workers (e.g. calling fetch() against the Worker's default export handler).
  2. That file's tests pass and log their output normally, including console.*/stdout emitted from inside the Worker.
  3. After that file's output, the process produces no further logs of any kind — no next file starting, no error.
  4. The process never exits on its own. Only an external timeout (CI job timeout, watchdog, etc.) kills it, many minutes later.
  5. Reproduced twice in a row under the same conditions (same file, same batch of files run together), so it doesn't look like a one-off flake.

This is easiest to hit when several test files run together in one long-lived process (either Vitest's normal multi-file run, or a sharding/batching tool that groups files into one process) — the hang isn't necessarily tied to being the last file in the run, just to a workerd-backed file's pool being torn down for one file while other files are still pending in the same run.

Suspected root cause

This matches the failure mode described in #15097 ("Miniflare#dispose() rejects with ERR_SERVER_NOT_RUNNING when called twice, skipping the rest of its cleanup"), which was closed not_planned but still describes a real defect: if dispose() is invoked more than once for the same Miniflare instance, the second call rejects early on an already-closed server and skips the remaining cleanup steps — including steps that stop things keeping the Node event loop alive (e.g. the dev registry file watcher, per that issue's own description). A leaked handle from a skipped cleanup step would explain a process that just sits there indefinitely with no output and no error, rather than crashing or exiting cleanly — which is exactly what's observed here.

#15085 fixed the ordering within a single dispose() call, so workerd termination isn't blocked behind other independent cleanup. It doesn't address a second dispose() call short-circuiting and skipping steps entirely. If anything in the pool's per-file or per-run teardown sequence can end up calling dispose() twice for the same instance, #15097's bug would still produce a silent hang even on 0.22.0.

Ask

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    package:vitestRelating to the Workers Vitest integration

    Type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions