Skip to content

[miniflare] Disable keep-alive timeout on the loopback server - #14850

Merged
NuroDev merged 6 commits into
cloudflare:mainfrom
exKAZUu:fix-loopback-keepalive-timeout
Jul 29, 2026
Merged

[miniflare] Disable keep-alive timeout on the loopback server#14850
NuroDev merged 6 commits into
cloudflare:mainfrom
exKAZUu:fix-loopback-keepalive-timeout

Conversation

@exKAZUu

@exKAZUu exKAZUu commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Fixes #14848.

The loopback server was a plain node:http server with default timeouts, so Node closed idle keep-alive sockets after the default server.keepAliveTimeout of 5 seconds. workerd pools and reuses connections to the loopback server, so this raced with connection reuse: Node closes an idle pooled socket (clean FIN) just as workerd sends the next request on it, and that request fails inside the Worker with Error: Network connection lost..

This change disables keepAliveTimeout on the loopback server, mirroring the undici pools used for dispatch in the opposite direction, which already disable their timeouts with a "Disable timeouts for local dev" comment.

Full analysis and evidence in #14848. Summary: with @cloudflare/vite-plugin and a large SSR module graph on a cold optimizer cache (~3,000 fetchModule loopback calls per page load with multi-second idle gaps), 2/3 cold starts failed with Network connection lost on stock Miniflare, while 4/4 succeeded with this change applied; the Node side logs no socket error on the failing runs, consistent with a clean keep-alive close losing the reuse race.


  • Tests
    • Tests included/updated: a deterministic regression test opens a raw keep-alive connection to the loopback server, idles past Node's default keepAliveTimeout (5s), and asserts a second request on the same socket still succeeds. Without the fix, Node deterministically closes the idle socket at ~5s and the test fails. The original end-to-end race was also manually verified via the A/B experiment in [miniflare] Loopback server uses Node's default 5s keepAliveTimeout, causing intermittent "Network connection lost" in workerd #14848 (patched Miniflare 4/4 cold-start successes vs 2/3 failures on stock; reproduced under both Node 24 and Bun).
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: dev-only internal transport fix with no user-facing API or behavior change other than removing spurious failures.

Open in Devin Review

workerd pools and reuses connections to the loopback server, and Node's
default keepAliveTimeout (5s) races with that reuse: Node closes an idle
pooled socket just as workerd sends the next request on it, which fails
inside the Worker with "Network connection lost". Disable the idle
timeouts, mirroring the undici dispatch pools in the opposite direction.

Fixes cloudflare#14848
@exKAZUu
exKAZUu requested a review from workers-devprod as a code owner July 26, 2026 01:50
@changeset-bot

changeset-bot Bot commented Jul 26, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3b7fdec

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 8 packages
Name Type
miniflare Patch
@cloudflare/deploy-helpers Patch
@cloudflare/pages-shared Patch
@cloudflare/remote-bindings Patch
@cloudflare/runtime-types Patch
@cloudflare/vite-plugin Patch
@cloudflare/vitest-pool-workers Patch
wrangler Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@workers-devprod
workers-devprod requested review from a team and NuroDev and removed request for a team July 26, 2026 01:50
@workers-devprod

workers-devprod commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • ✅ @cloudflare/wrangler
Show detailed file reviewers

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@NuroDev NuroDev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a deterministic regression test for this?

Also, is headersTimeout = 0 necessary? That controls how long clients may take to send request headers & is unrelated to idle keep-alive connections.

…ession test

headersTimeout only limits how long a client may take to send request
headers; it does not apply to idle keep-alive sockets, so it is not
needed for this fix. Verified empirically: with keepAliveTimeout = 0 and
a short headersTimeout, an idle keep-alive socket survives past the
headersTimeout and still serves a second request.

The new test talks to the loopback server over a raw keep-alive socket,
idles past Node's default keepAliveTimeout of 5 seconds, and asserts a
second request still succeeds. Without the fix the socket is closed
deterministically at ~5s and the test fails.
@exKAZUu

exKAZUu commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

@NuroDev Thanks for the review — both points addressed.

Regression test: Added a deterministic one in packages/miniflare/test/index.spec.ts. It opens a raw keep-alive connection to the loopback server, sends a request, idles past Node's default keepAliveTimeout (5s), and asserts a second request on the same socket still succeeds. This doesn't depend on the workerd reuse race: without the fix, Node closes the idle socket at ~5s on a timer, so the test fails deterministically (verified locally in both directions: fails in ~7s without the fix, passes with it).

headersTimeout: You're right — it limits how long a client may take to send request headers and doesn't govern idle keep-alive sockets, so it isn't needed for this fix. I confirmed empirically that with keepAliveTimeout = 0 and a deliberately short headersTimeout, an idle keep-alive socket survives past the headersTimeout and still serves a second request. Dropped it (I'd originally included it only to mirror the undici pools on the dispatch side,which disable their headersTimeout/bodyTimeout).

@exKAZUu
exKAZUu requested a review from NuroDev July 28, 2026 02:51
@edmundhung
edmundhung requested review from edmundhung and removed request for NuroDev July 28, 2026 15:22
@github-project-automation github-project-automation Bot moved this to In Review in workers-sdk Jul 28, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jul 28, 2026

Copy link
Copy Markdown
@cloudflare/autoconfig

npm i https://pkg.pr.new/@cloudflare/autoconfig@14850

@cloudflare/config

npm i https://pkg.pr.new/@cloudflare/config@14850

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@14850

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/@cloudflare/deploy-helpers@14850

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@14850

miniflare

npm i https://pkg.pr.new/miniflare@14850

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@14850

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@14850

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@14850

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@14850

@cloudflare/workers-auth

npm i https://pkg.pr.new/@cloudflare/workers-auth@14850

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@14850

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@14850

wrangler

npm i https://pkg.pr.new/wrangler@14850

commit: 3b7fdec

Comment thread packages/miniflare/test/index.spec.ts Outdated

@workers-devprod workers-devprod left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codeowners reviews satisfied

@github-project-automation github-project-automation Bot moved this from In Review to Approved in workers-sdk Jul 28, 2026
@exKAZUu

exKAZUu commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Note: The C3 E2E (pnpm@11.5.1, Linux) - cli failure (https://github.com/cloudflare/workers-sdk/actions/runs/30452652992/job/90578247605?pr=14850) looks unrelated to this PR

@exKAZUu exKAZUu changed the title fix(miniflare): disable keep-alive timeout on the loopback server [miniflare] Disable keep-alive timeout on the loopback server Jul 29, 2026
@NuroDev
NuroDev merged commit 5c25cfe into cloudflare:main Jul 29, 2026
69 of 71 checks passed
@github-project-automation github-project-automation Bot moved this from Approved to Done in workers-sdk Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[miniflare] Loopback server uses Node's default 5s keepAliveTimeout, causing intermittent "Network connection lost" in workerd

3 participants