Skip to content

[wrangler] createTestHarness() under Bun: listen() succeeds, then server.fetch() and every request to the returned URL hang forever with no error #15717

Description

@oddharsh

What versions & operating system are you using?

wrangler 4.131.2 (also the 2026-09-15 main build, pkg.pr.new f9e7727), miniflare 5.20260820.0-alpha
bun 1.4.2 / node v26.8.2
macOS 26 arm64

Please provide a link to a minimal reproduction

Three files, inline (happy to push them as a repo if that is easier):

// worker.js
export default { fetch: () => new Response("ok") };
// wrangler.jsonc
{ "name": "harness-probe", "main": "worker.js", "compatibility_date": "2026-09-01" }
// probe.mjs
import { createTestHarness } from "wrangler";
const t0 = Date.now(); const step = (s) => console.log(`[${Date.now() - t0}ms] ${s}`);
step(`runtime=${process.versions.bun ? "bun " + process.versions.bun : "node " + process.version}`);
const server = createTestHarness({ workers: [{ configPath: "./wrangler.jsonc" }] });
const { url } = await server.listen(); step(`listen -> ${url}`);
const w = await server.getWorker().fetch("/"); step(`getWorker().fetch("/") -> ${w.status}`);
const race = (p, label) => Promise.race([
  p.then(async (r) => step(`${label} -> ${r.status}`)),
  new Promise((res) => setTimeout(() => { step(`${label} -> no response after 5s`); res(); }, 5000)),
]);
await race(server.fetch("/"), 'server.fetch("/")');
await race(fetch(url), `fetch(url)`);
await server.close(); process.exit(0);

Describe the Bug

Run node probe.mjs and bun probe.mjs:

node 26 bun 1.4.2
listen() 211ms, returns a URL 204ms, returns a URL
getWorker().fetch("/") 200 200
server.fetch("/") 200 no response, ever
fetch(url) from the test 200 no response, ever

Under Bun nothing throws, nothing is logged, and the process only ends when the caller's own timeout fires. In a real suite that reads as every route timing out: our route oracle (168 routes through the harness) reports 167 hard failures in 9m23s under bun and 168 passes in 6s under node, with identical config.

Cause, as far as I can trace it: the URL from listen() and server.fetch() both go through the ProxyWorker, which queues requests until the ProxyController delivers play with the upstream in cf.hostMetadata. That is sent through miniflare's dispatchFetch(), which attaches the blob as MF-CF-Blob from a custom undici Dispatcher and calls fetch(url, { dispatcher }). Bun's fetch ignores the dispatcher option (oven-sh/bun#39247, fix pending in oven-sh/bun#39250), so the header never lands, request.cf?.hostMetadata is undefined in the ProxyWorker, and the queue never flushes. getWorker().fetch() works because it dispatches to the runtime miniflare directly with the route on MF-Route-Override, no ProxyWorker in the path. Delivering the play by hand under bun makes the same harness flush and serve, which is what convinced me it is the door rather than the config.

Wrangler already knows it does not support Bun (logPossibleBugMessage prints "Wrangler does not support the Bun runtime" when a command errors), but that message only fires on an error, and this path never produces one.

Suggestion: refuse up front. If process.versions.bun is set, have createTestHarness() (or listen()) throw a UserError naming the reason and pointing at bun#39247, the same way wrangler check startup refuses. A hang with no message is the worst version of unsupported. I am happy to open the PR with a test if that shape is acceptable; I held off because the right place for the guard (construction vs listen()) and whether to warn or throw felt like your call, and because the guard becomes removable once bun#39250 ships.

Please provide any relevant error logs

None. That is the bug.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    package:wranglerRelating to the `wrangler` package

    Type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions