Skip to content

Dev server restart wedges permanently under @cloudflare/vite-plugin when a request straddles the restart (bundled-dev/rolldown pipeline implicated) #2481

Description

@zhorton34

Versions

vite-plus 0.2.5 (Vite core 8.1.5), @cloudflare/vite-plugin 1.52.1, wrangler 4.123.0, node 24.x, darwin-arm64 (also reproduces on GitHub Actions macos-14)

Summary

Cross-filed with cloudflare/workers-sdk#15241 (full mechanism and OS-level evidence there). Filing here because the permanent wedge reproduces only when the Vite+/rolldown dev pipeline is active — a plain-Vite scaffold at identical pins does not wedge in 8 escalating-fidelity trials — so vite-plus is a co-suspect by elimination.

When server.restart() runs while an HTTP request is in flight, the cloudflare plugin's post-swap handshake (fetchWorkerExportTypesminiflare.dispatchFetch) never resolves: the straddling request is left permanently in-flight inside workerd (CLOSE_WAIT entry socket in 7/7 hangs), and the export-types fetch wedges behind it. configureServer never returns, server.restart() never settles, and the old server is never closed. Traffic during restart is necessary and sufficient (hang 9/9 with a poll loop; clean 14/14 without).

Why vite-plus specifically

  • The wedged node process runs rolldown worker threads at hang time (sample captures available) — i.e. the bundled-dev serving path, not plain per-module transform.
  • Plain Vite + the same cloudflare plugin, same options swap, same D1 binding, same tight polling: the mid-swap fetch failed abort signature reproduces, but the export-types fetch still resolves (~3s). Under vite-plus it never resolves.
  • The in-flight JsRpcTarget frames in every wedged workerd sample are consistent with a straddling request holding a multi-roundtrip module-runner/bundle RPC open across the Miniflare options swap — plausibly the bundled-dev "keep Miniflare and related resources alive during mid-serve builds" behavior interacting with a client-aborted request that workerd never cancels.

Repro

Deterministic 9/9 via a Pioneer framework scaffold (github.com/find-how/pioneer.find.how — pioneer new, pioneer dev, curl poll loop, config-triggered restart). Trial harness and captured evidence (debuglogs, sample, lsof) available on request; happy to help reduce further.

Downstream mitigation in place

We now 503-gate incoming requests during restart, which removes the trigger — but the non-settling handshake under bundled-dev remains reproducible upstream behavior worth a cancellation/drain fix at the source.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions