cloudflare / cloudflare/workers-sdk
wrangler dev exits 1 mid-run on "Network connection lost." from ProxyWorker, and prints an empty `✘ [ERROR]`
- Dominant language
- TypeScript
- Stars
- 4.5k
- Forks
- 1.5k
- Avg merge
- 3d 8h
- Merged PRs (30d)
- 186
Description
### Which Cloudflare product(s) does this pertain to?
Wrangler core (`wrangler dev`)
### What versions are you using?
- wrangler `4.127.0` (also reproduced conceptually on `4.127.1`; changelog shows only dependency bumps)
- miniflare `5.20260826.0-alpha`
- Node 22, Ubuntu (GitHub Actions `ubuntu-latest`, 2 vCPU)
### Describe the issue
Two separate problems, one report, because the second hides the first.
**1. A dropped client connection kills the dev server.**
`wrangler dev` exits with code 1 in the middle of a run when the ProxyWorker reports `Network connection lost.`. This happens under a Playwright suite that navigates rapidly: a page load is followed immediately by the next `page.goto`, which cancels the still-in-flight subresource requests of the previous page. The ProxyWorker surfaces that as an error, `ProxyController` treats *any* ProxyWorker error as fatal, and the dev server dies — taking the whole test run with it.
A client aborting a request is normal browser behaviour, not a dev-server fault. It should not terminate `wrangler dev`.
**2. The error printed to the terminal is empty.**
All the user sees is:
```
✘ [ERROR]
```
No message, no stack — just the marker and a blank line. The actual cause is only in the debug log file:
```
Error in ProxyController: Error inside ProxyWorker
Error
at castErrorCause (wrangler-dist/cli.js:180603:20)
at ProxyController2.emitErrorEvent (cli.js:326752:20)
at ProxyController2.onProxyWorkerMessage (cli.js:326629:18)
at PROXY_CONTROLLER (cli.js:326356:24)
at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
at async #handleLoopbackCustomFetchService (miniflare/dist/src/index.js:112343:22)
at async #handleLoopback (miniflare/dist/src/index.js:112726:20) {
cause: {
name: 'Error',
message: 'Network connection lost.',
stack: 'Error: Network connection lost.'
}
}
```
The outer `Error` carries **no `message`** — the text lives in `.cause`. `Logger.error()` formats the outer `message` through `esbuild.formatMessagesSync`, and an empty string produces exactly `✘ [ERROR] ` with nothing after it.
In CI this is expensive: the log file lives in `~/.config/.wrangler/logs/` on the runner and is gone once the job ends, so the only surviving evidence is a blank error line. It took capturing that file as a build artifact to find out what had actually happened.
### Suggested fixes
- Do not treat a lost client connection as fatal to the dev server. At most, log it.
- When an error has no `message`, fall back to `cause.message` (or the full chain) before printing. An empty `✘ [ERROR] ` line is strictly worse than no line at all — it says something failed while withholding what.
### Steps to reproduce
Intermittent — roughly 1 in 7 runs in our CI, never reproduced locally. Run a Playwright suite against `wrangler dev` where each test performs a navigation immediately after a redirect completes (`await expect(page).toHaveURL(...)` and then `page.goto(...)`), so subresource requests of the first page are cancelled by the second navigation. Awaiting `networkidle` between the two makes it stop occurring for us, which is consistent with the cancelled-request explanation but is a workaround in the test, not a fix.
Contributor guide
Research direction
Start by locating ProxyController, especially onProxyWorkerMessage and emitErrorEvent, then inspect Logger.error and the Wrangler dev Playwright reproduction described in the issue. Run the intermittent navigation scenario and verify that a lost client connection no longer exits the server and that errors without a message print their cause instead of an empty marker.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- playwright, typescript
- Domain
- backend, cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100