voidzero-dev / voidzero-dev/vite-plus

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

Open
#2,481 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
5.8k
Forks
262
Avg merge
1d 34m
Merged PRs (30d)
135

Description

### 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.

Contributor guide

Open the contributing guide

Research direction

Start at the bundled-dev/rolldown dev-server path and trace server.restart(), configureServer, and the fetchWorkerExportTypes → miniflare.dispatchFetch handshake. Reproduce with the Pioneer scaffold and curl poll loop while a request straddles restart, then compare against plain Vite. Done means the handshake settles and the old server closes without requiring the downstream 503 gate.

Written by the indexing model from the issue text.

Assessment

Tech stack
nodejs, rust
Domain
devtools
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
32/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.