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") };
// 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.
What versions & operating system are you using?
wrangler 4.131.2 (also the 2026-09-15
mainbuild, pkg.pr.new f9e7727), miniflare 5.20260820.0-alphabun 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):
Describe the Bug
Run
node probe.mjsandbun probe.mjs:listen()getWorker().fetch("/")server.fetch("/")fetch(url)from the testUnder 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()andserver.fetch()both go through the ProxyWorker, which queues requests until the ProxyController deliversplaywith the upstream incf.hostMetadata. That is sent through miniflare'sdispatchFetch(), which attaches the blob asMF-CF-Blobfrom a custom undiciDispatcherand callsfetch(url, { dispatcher }). Bun'sfetchignores thedispatcheroption (oven-sh/bun#39247, fix pending in oven-sh/bun#39250), so the header never lands,request.cf?.hostMetadatais undefined in the ProxyWorker, and the queue never flushes.getWorker().fetch()works because it dispatches to the runtime miniflare directly with the route onMF-Route-Override, no ProxyWorker in the path. Delivering theplayby 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 (
logPossibleBugMessageprints "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.bunis set, havecreateTestHarness()(orlisten()) throw aUserErrornaming the reason and pointing at bun#39247, the same waywrangler check startuprefuses. 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 vslisten()) 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.