Skip to content

chore: upgrade Bun to 1.4.4 - #178

Merged
rexdotsh merged 2 commits into
mainfrom
chore/bun-1.4.4
Oct 22, 2026
Merged

rexdotsh merged 2 commits into
mainfrom
chore/bun-1.4.4

Conversation

@rexdotsh

Copy link
Copy Markdown
Owner

Summary

Bun 1.4.3 → 1.4.4 (release notes). The first deploy after merge moves production onto the new runtime.

This PR also includes a small fix, needed because Bun 1.4.4 rewrote its built-in ws to behave like npm ws on Node: an "error" event with no listener now throws as an uncaught exception. The Codex WebSocket relay detaches its listeners before terminating a socket that is still in its handshake (request abort, 15 s connect timeout) and while a socket sits idle in the session cache. On 1.4.4 both paths exit the process. The fix adds one permanent no-op "error" listener per relay socket, and a regression test that uses the real ws against a local server that never answers the upgrade. The test fails on 1.4.4 without the fix and passes on 1.4.3.

Pins

  • package.json packageManager: bun@1.4.4 (CI reads this through setup-bun's bun-version-file)
  • Dockerfile: oven/bun:1.4.4
  • @types/bun: kept at 1.4.3. @types/bun@1.4.4 isn't on npm yet: latest is still 1.4.3, while bun-types@1.4.4 is published. Typecheck passes with 1.4.3 types. Bump it in a follow-up once it's published.
  • bun.lock: regenerated with 1.4.4 (bun install and bun install --force). Both left it byte-identical. A lockfile resolved from scratch would move to lockfileVersion: 2 and re-resolve caret ranges (for example @libsql/* 0.5.22 → 0.5.29 and @types/node 25 → 26), so this PR keeps the existing lockfile. The new libc lockfile field is picked up when those packages are next updated.
  • Deploy and build workflows are unchanged. They already take the version from package.json.

Risk review (1.4.4 notes vs Kleis)

Area Kleis exposure Result
ws rewrite: unhandled error throws Codex relay (import WebSocket from "ws" resolves to Bun's built-in) Crash, fixed here. Reproduced on 1.4.4: abort during handshake → WebSocket was closed before the connection was established, exit 1; invalid UTF-8 frame on an idle cached socket → exit 1. Neither crashes on 1.4.3, and neither crashes on 1.4.4 with the fix.
close/error semantics, 1005/1006 Relay classifies close codes and error messages Handshake failures now fire error then close 1006 (was close 1002 with an English reason). connectWebSocket settles on the first event, so a 403 is now logged as unexpected_server_response, HTTP 403 (the message matches npm ws: Unexpected server response: 403). ECONNREFUSED now has error.code. A protocol error after open now arrives as socket_error (counted toward fallback after 5 failures) instead of socket_closed_before_terminal 1007/1009 (immediate fallback). TCP loss after open is still close 1006 with immediate fallback. The relay always passes an explicit close code (1000 "done"), so the change to report 1005 doesn't apply.
Closing handshake waits (30 s timeout) Idle-expiry close(1000) A socket can linger in CLOSING for up to 30 s if upstream doesn't reply. It's already out of the cache and has the no-op error listener.
Default maxPayload 16 → 100 MiB Relay The relay already rejects frames over 4 MiB (relay_frame_too_large). Frames between 16 and 100 MiB now reach that check instead of a ws 1009 close.
fetch / streams / Headers SSE relay, proxy hot path, @libsql/client Differential script (Headers iteration and order, duplicates, set-cookie, delete-during-iteration, clone() + getReader, DecompressionStream via pipeThrough, TransformStream): byte-identical output on 1.4.3 and 1.4.4. Kleis never sets keepalive: false.
@libsql/client DB libsql:// resolves to HTTP (preferHttp), so fetch is used, not WebSockets. file: migrations, server start and /healthz pass on 1.4.4.
Bun.Glob extglob / brace ranges scripts/test-ci.ts only The pattern has no ?(, *(, +(, @(, !( or {a..b}. 1.4.3 and 1.4.4 return the same 35 files (same hash).
JSC — No +bigint, toLocaleString, or stack-column parsing in src.
process.on duplicates, PID 1 exit codes Graceful shutdown install() runs once. Compose uses init: true, so Bun isn't PID 1. SIGTERM drain still exits 0.
Package manager rejects unknown flags Dockerfile / CI Only --frozen-lockfile and --production are used, and both are valid.

Validation (all with Bun 1.4.4, linux-x64)

  • bun install --frozen-lockfile, bun fix (no changes), bun lint, bun typecheck: pass
  • bun test: 320 pass / 12 skip / 0 fail (the skips are the existing skipIf provider tables). --randomize × 5 seeds: same counts, 0 fail
  • bun scripts/test-ci.ts: 304 + 16 pass, exit 0
  • bun run build: pass. The built server against a migrated temp file: DB: /healthz 200, /api.json 200, bad key 401, SIGTERM drained and exited
  • Docker release target: builds, bun --version → 1.4.4 (1.4.4+38afee6c8), container /healthz 200. Built on amd64 only; production is arm64.

Benchmark (main on 1.4.3 vs this branch on 1.4.4)

In-process Hono app with the real proxy routes, upstream mocked by a local Bun.serve reached through real fetch. Each figure is the median of 150 requests per run, then the median of 5 interleaved runs, in ms:

Case 1.4.3 1.4.4 Δ
Codex SSE relay (1,002 events, 338 KiB) 2.826 2.679 −5.2%
Claude SSE transform (1,005 events) 1.137 1.153 +1.4%
Codex 2 MiB request round trip 3.954 3.957 +0.1%
Claude 2 MiB request round trip 4.513 4.483 −0.7%

No regressions. Except for the Codex SSE relay, every difference is within run-to-run noise.

Not covered

  • No live provider traffic, and no live Codex WebSocket session (CODEX_WEBSOCKET_ENABLED is off by default).

Bun 1.4.4's built-in ws throws an "error" event that has no listener,
like npm ws on Node. The relay detaches its listeners before terminating
a socket mid-handshake (request abort or connect timeout) and while a
socket sits idle in the session cache, so either path crashed the process.
@rexdotsh
rexdotsh merged commit f62abc3 into main Oct 22, 2026
5 checks passed
@rexdotsh
rexdotsh deleted the chore/bun-1.4.4 branch October 22, 2026 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant