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 (fetchWorkerExportTypes → miniflare.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.
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 (fetchWorkerExportTypes→miniflare.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.configureServernever 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
samplecaptures available) — i.e. the bundled-dev serving path, not plain per-module transform.fetch failedabort signature reproduces, but the export-types fetch still resolves (~3s). Under vite-plus it never resolves.JsRpcTargetframes 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.