Bun Upstream Bugs
Bun upstream bugs (to file at oven-sh/bun)
Section titled “Bun upstream bugs (to file at oven-sh/bun)”Two bugs surfaced while hardening the Slingshot Bun runtime
(../src/index.ts). Both reproduce on Bun 1.3.11 (macOS arm64). Captured here
so we can file upstream issues with minimal repros.
Bug 1: server.stop() hangs with active WebSocket connections
Section titled “Bug 1: server.stop() hangs with active WebSocket connections”- Bun version: 1.3.11
- Platform: macOS arm64 (also seen on Linux x64 in CI)
- Symptom: A bare
Bun.servewith WS upgrade, given an open client connection, will hang indefinitely if youawait server.stop()orawait server.stop(true). The promise never resolves even after the client-sidews.close()has fully completed. - Expected:
server.stop()resolves once in-flight requests/sockets have drained.server.stop(true)(force) terminates active connections and resolves promptly. - Workaround: explicitly drain by calling
ws.close(code, reason)for every active socket, awaiting a per-socket promise that resolves from the server’sclosehandler, then callingserver.stop(). See theactiveWebSocketsset +waitForWebSocketDrainlogic in../src/index.ts.
Minimal repro
Section titled “Minimal repro”const server = Bun.serve({ port: 0, fetch(req, s) { return s.upgrade(req) ? undefined : new Response('no'); }, websocket: { open() {}, message() {}, close() {} },});const ws = new WebSocket(`ws://localhost:${server.port}`);await new Promise(r => (ws.onopen = r));ws.close();await new Promise(r => (ws.onclose = r));await server.stop(true); // hangs foreverBug 2: WebSocket close code 1001 collapses to 1006 under bun test
Section titled “Bug 2: WebSocket close code 1001 collapses to 1006 under bun test”- Bun version: 1.3.11
- Platform: macOS arm64
- Symptom: Server-side
ws.close(1001, 'reason')immediately followed byserver.stop()delivers a 1001 close frame with the supplied reason when run underbun run, but underbun testthe client frequently observes1006(abnormal closure) with an empty reason instead. The runtime’s ownclosehandler still fires with 1001 + reason on the server side; only the wire frame is mis-delivered to the client. - Expected: client receives
code === 1001and the reason string, on bothbun runandbun test, regardless of how soonserver.stop()is called. - Workaround: per socket, await a promise that resolves from inside the
closehandler before invokingserver.stop(). See drain logic in../src/index.tsand the test-side handling in../tests/ws-pubsub.test.ts(the test now accepts either 1001 or 1006 because of this bug).
Minimal repro
Section titled “Minimal repro”const server = Bun.serve({ port: 0, fetch(req, s) { return s.upgrade(req) ? undefined : new Response('no'); }, websocket: { open(ws) { ws.close(1001, 'going away'); }, message() {}, close() {}, },});const ws = new WebSocket(`ws://localhost:${server.port}`);const ev = await new Promise<CloseEvent>(r => (ws.onclose = r));console.log(ev.code, ev.reason); // bun run: 1001 'going away' | bun test: 1006 ''await server.stop(true);Run with bun test repro.test.ts (wrap in it(...)) to see the 1006 result.