fix(ci): stop one aborted upload from killing wrangler dev - #20
Merged
Conversation
The e2e job died mid-run and passed on a re-run. It was not flaky infrastructure and it was not the OOM killer: `wrangler dev` exits on a SINGLE failed proxied request, and reports it as `✘ [ERROR]` with an empty message. Wrangler forwards every request through its own ProxyWorker. When that inner fetch rejects, the ProxyWorker reports it to the ProxyController, `DevEnv.handleErrorEvent` finds no matching non-fatal case and re-emits it as a top-level `error`; the dev command is `await events.once(devEnv, "teardown")`, and Node's `events.once` rejects on `error`, so the command throws and the process exits 1. The cause crossed a Worker boundary, so it is a plain object rather than an Error, and `castErrorCause` wraps it in `new Error()` -- message "". Hence a fatal exit that names nothing. The rejection is `Network connection lost.`, raised when a client disconnects while its request body is still uploading. Playwright does that every time it closes a page with a form POST in flight. Confirmed by reduction: five aborted uploads against a bare Worker kill wrangler 4.121.0 with the exact CI signature, and survive it patched. So the failure was one browser-side race taking down the server for the rest of the suite -- seventy `ERR_CONNECTION_REFUSED` failures, three attempts each, none of them naming the event. Patch wrangler with upstream's own fix (cloudflare/workers-sdk#15207, issue #15203): report the failed request and keep serving. Two call sites, because wrangler bundles the ProxyController twice. Remove the patch when a release carries the fix -- a version bump that leaves it here fails the install rather than applying quietly. `patches/**` joins the js paths filter: a patched dependency is application behaviour, and editing it must run the suite that proves it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
yashau
force-pushed
the
claude/flaky-e2e-ci-wrangler-730456
branch
from
August 19, 2026 21:44
3c0c5f8 to
f686ea9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The
e2ejob died mid-run and passed on a re-run. It was not flaky infrastructure and it was not the OOM killer.What actually happened
wrangler devexits on a single failed proxied request, and reports it as✘ [ERROR]with an empty message.Every request goes through wrangler's own ProxyWorker. When that inner fetch rejects:
ProxyController;DevEnv.handleErrorEventfinds no matching non-fatal case and re-emits it as a top-levelerror;await events.once(devEnv, "teardown")— and Node'sevents.oncerejects onerror, so the command throws and the process exits 1;Error;castErrorCausewraps it innew Error(), whose message is"".Hence a fatal exit that names nothing. From the failing run:
code=1 signal=null, no OOM kill recorded, 8 GB free.The rejection is
Network connection lost., raised when a client disconnects while its request body is still uploading. Playwright does that every time it closes a page with a form POST in flight — which is why it landed roughly twenty tests in, and why re-running "fixed" it.Confirmed by reduction
Five aborted uploads against a bare Worker (no assets, no app code) reproduce the exact CI signature on wrangler 4.121.0, and survive it patched:
✘ [ERROR](empty), exit 1, port unboundError proxying request to the local Worker: Network connection lost., still servingAborting mid-response is survivable; only mid-request-body is fatal.
The fix
patches/wrangler@4.121.0.patchapplies upstream's own fix — cloudflare/workers-sdk#15207 for #15203: report the failed request and keep serving. Two call sites, because wrangler bundles theProxyControllertwice (its own, and the remote-bindings copy).Both upstream PRs for this are still open and
mainis still unfixed, so there is no release to upgrade to. Remove the patch when one carries the fix — a version bump that leaves it here fails the install rather than applying quietly, which is the intended prompt to go and check.patches/**joins thejspaths filter: a patched dependency is application behaviour, and editing it must run the suite that proves it still works.Testing
rbac-uivisibility assertion that passed on retry),wrangler devalive throughout.pnpm install --frozen-lockfilereapplies the patch from a clean store; verified 2 patched call sites, 0 fatal ones.pnpm fmt:checkclean; pre-commit hooks (typos, zizmor, actionlint, lint-js-repo, fmt-js) all pass.🤖 Generated with Claude Code