Which Cloudflare product(s) does this pertain to?
@cloudflare/vite-plugin
Versions
@cloudflare/vite-plugin 1.52.1 (latest at time of filing), wrangler 4.123.0, @cloudflare/workers-types 5.20260816.1, vite-plus 0.2.5 (Vite 8.1.5 core), node 24.x, darwin-arm64 (also reproduces on GitHub Actions macos-14)
Describe the bug
When server.restart() runs while an HTTP request is in flight to the dev server, the restart can wedge permanently: the plugin's module-scope shared context reuses the live Miniflare instance, setOptions completes ("Miniflare is ready", module runners re-initialize), but the next step — getCurrentWorkerNameToExportTypesMap → fetchWorkerExportTypes → miniflare.dispatchFetch(GET_EXPORT_TYPES_PATH, …) (dist/index.mjs:66627-66630, called from devPlugin.configureServer at :83080) — never resolves. configureServer never returns → _createServer never returns → server.restart() never settles. The old server's close() is never reached (verified by instrumentation), so the stale workerd lives on indefinitely.
Traffic during the restart is both necessary and sufficient (2×2 differential, 9/9 reproduction with traffic, 14/14 clean without):
- restart with a concurrent curl poll loop → hangs 3/3 (fresh app, no D1 activity at all)
- identical restart with no in-flight traffic → completes 3/3 (plus 11/11 across earlier trials)
OS-level signature at hang time, consistent across 7/7 hangs:
lsof on workerd shows exactly one entry-socket connection in CLOSE_WAIT whose peer no longer exists (the aborted straddling request) — never present in non-hang runs
- workerd
sample shows in-flight JsRpcTarget frames (a straddling request holding a multi-roundtrip module-runner RPC across the swap); workerd does not cancel a request on client disconnect
- node side logs
Internal server error: fetch failed from Miniflare.dispatchFetch via undici at restart time
- plugin debuglog's last line ever:
Fetching export types for worker "…" — the watchdog we added fires 30s later, every time
Reproduction and an honest scoping caveat
A plain-Vite worker scaffold at the same pins did NOT reproduce the permanent wedge in 8 escalating-fidelity trials (including a real options swap via config-triggered restart, a D1 binding, a 40-module chain, and tight polling — the mid-swap fetch failed abort signature reproduces, but export-types still resolves in ~3s). The permanent wedge reproduces only with Vite's bundled-dev/rolldown pipeline active (the wedged node process runs rolldown worker threads). Your 1.48.0 changelog notes recent work on exactly this codepath ("compatibility with Vite's experimental.bundledDev option, keeping Miniflare and related resources alive during mid-serve builds") — the interaction of that keep-alive behavior with an in-flight request across setOptions looks like the fault line.
Deterministic repro available: a Pioneer framework scaffold (pioneer new from github.com/find-how/pioneer.find.how, which runs the cloudflare plugin under the bundled-dev pipeline) + a curl poll loop + a config-triggered restart reproduces 9/9. Happy to provide the trial harness and captured sample/lsof evidence.
Nearest existing issues checked (not duplicates): #11171 (Bun-specific restart hang), withastro/astro#17492 (dispatchFetch POST hang).
Impact / downstream mitigation
Any framework or tooling that restarts the dev server while a client is polling (health checks, test harnesses, HMR-adjacent tooling) can permanently wedge the dev session with no error surfaced. We now mitigate downstream by gating incoming requests with a 503 during restart, which eliminates the trigger — but the underlying non-settling dispatchFetch and never-closed old server remain upstream behavior. A cancellation/timeout on the export-types fetch, or draining/aborting in-flight requests before setOptions, would fix the class.
Which Cloudflare product(s) does this pertain to?
@cloudflare/vite-plugin
Versions
@cloudflare/vite-plugin 1.52.1 (latest at time of filing), wrangler 4.123.0, @cloudflare/workers-types 5.20260816.1, vite-plus 0.2.5 (Vite 8.1.5 core), node 24.x, darwin-arm64 (also reproduces on GitHub Actions macos-14)
Describe the bug
When
server.restart()runs while an HTTP request is in flight to the dev server, the restart can wedge permanently: the plugin's module-scope shared context reuses the live Miniflare instance,setOptionscompletes ("Miniflare is ready", module runners re-initialize), but the next step —getCurrentWorkerNameToExportTypesMap→fetchWorkerExportTypes→miniflare.dispatchFetch(GET_EXPORT_TYPES_PATH, …)(dist/index.mjs:66627-66630, called fromdevPlugin.configureServerat:83080) — never resolves.configureServernever returns →_createServernever returns →server.restart()never settles. The old server'sclose()is never reached (verified by instrumentation), so the stale workerd lives on indefinitely.Traffic during the restart is both necessary and sufficient (2×2 differential, 9/9 reproduction with traffic, 14/14 clean without):
OS-level signature at hang time, consistent across 7/7 hangs:
lsofon workerd shows exactly one entry-socket connection in CLOSE_WAIT whose peer no longer exists (the aborted straddling request) — never present in non-hang runssampleshows in-flightJsRpcTargetframes (a straddling request holding a multi-roundtrip module-runner RPC across the swap); workerd does not cancel a request on client disconnectInternal server error: fetch failedfromMiniflare.dispatchFetchvia undici at restart timeFetching export types for worker "…"— the watchdog we added fires 30s later, every timeReproduction and an honest scoping caveat
A plain-Vite worker scaffold at the same pins did NOT reproduce the permanent wedge in 8 escalating-fidelity trials (including a real options swap via config-triggered restart, a D1 binding, a 40-module chain, and tight polling — the mid-swap
fetch failedabort signature reproduces, but export-types still resolves in ~3s). The permanent wedge reproduces only with Vite's bundled-dev/rolldown pipeline active (the wedged node process runs rolldown worker threads). Your 1.48.0 changelog notes recent work on exactly this codepath ("compatibility with Vite'sexperimental.bundledDevoption, keeping Miniflare and related resources alive during mid-serve builds") — the interaction of that keep-alive behavior with an in-flight request acrosssetOptionslooks like the fault line.Deterministic repro available: a Pioneer framework scaffold (
pioneer newfrom github.com/find-how/pioneer.find.how, which runs the cloudflare plugin under the bundled-dev pipeline) + a curl poll loop + a config-triggered restart reproduces 9/9. Happy to provide the trial harness and capturedsample/lsofevidence.Nearest existing issues checked (not duplicates): #11171 (Bun-specific restart hang), withastro/astro#17492 (dispatchFetch POST hang).
Impact / downstream mitigation
Any framework or tooling that restarts the dev server while a client is polling (health checks, test harnesses, HMR-adjacent tooling) can permanently wedge the dev session with no error surfaced. We now mitigate downstream by gating incoming requests with a 503 during restart, which eliminates the trigger — but the underlying non-settling
dispatchFetchand never-closed old server remain upstream behavior. A cancellation/timeout on the export-types fetch, or draining/aborting in-flight requests beforesetOptions, would fix the class.