Repository navigation
chore: upgrade Bun to 1.4.4 - #178
Merged
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
wsto behave like npmwson 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 realwsagainst 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.jsonpackageManager:bun@1.4.4(CI reads this throughsetup-bun'sbun-version-file)Dockerfile:oven/bun:1.4.4@types/bun: kept at1.4.3.@types/bun@1.4.4isn't on npm yet:latestis still 1.4.3, whilebun-types@1.4.4is 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 installandbun install --force). Both left it byte-identical. A lockfile resolved from scratch would move tolockfileVersion: 2and re-resolve caret ranges (for example@libsql/*0.5.22 → 0.5.29 and@types/node25 → 26), so this PR keeps the existing lockfile. The newlibclockfile field is picked up when those packages are next updated.package.json.Risk review (1.4.4 notes vs Kleis)
wsrewrite: unhandlederrorthrowsimport WebSocket from "ws"resolves to Bun's built-in)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/errorsemantics, 1005/1006errorthenclose1006 (wasclose1002 with an English reason).connectWebSocketsettles on the first event, so a 403 is now logged asunexpected_server_response, HTTP 403(the message matches npmws:Unexpected server response: 403). ECONNREFUSED now haserror.code. A protocol error afteropennow arrives assocket_error(counted toward fallback after 5 failures) instead ofsocket_closed_before_terminal1007/1009 (immediate fallback). TCP loss afteropenis stillclose1006 with immediate fallback. The relay always passes an explicit close code (1000 "done"), so the change to report 1005 doesn't apply.close(1000)maxPayload16 → 100 MiBrelay_frame_too_large). Frames between 16 and 100 MiB now reach that check instead of a ws 1009 close.@libsql/clientset-cookie, delete-during-iteration,clone()+getReader,DecompressionStreamviapipeThrough, TransformStream): byte-identical output on 1.4.3 and 1.4.4. Kleis never setskeepalive: false.@libsql/clientlibsql://resolves to HTTP (preferHttp), so fetch is used, not WebSockets.file:migrations, server start and/healthzpass on 1.4.4.Bun.Globextglob / brace rangesscripts/test-ci.tsonly?(,*(,+(,@(,!(or{a..b}. 1.4.3 and 1.4.4 return the same 35 files (same hash).+bigint,toLocaleString, or stack-column parsing insrc.process.onduplicates, PID 1 exit codesinstall()runs once. Compose usesinit: true, so Bun isn't PID 1. SIGTERM drain still exits 0.--frozen-lockfileand--productionare 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: passbun test: 320 pass / 12 skip / 0 fail (the skips are the existingskipIfprovider tables).--randomize× 5 seeds: same counts, 0 failbun scripts/test-ci.ts: 304 + 16 pass, exit 0bun run build: pass. The built server against a migrated tempfile:DB:/healthz200,/api.json200, bad key 401, SIGTERM drained and exitedreleasetarget: builds,bun --version→1.4.4(1.4.4+38afee6c8), container/healthz200. 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.servereached through realfetch. Each figure is the median of 150 requests per run, then the median of 5 interleaved runs, in ms:No regressions. Except for the Codex SSE relay, every difference is within run-to-run noise.
Not covered
CODEX_WEBSOCKET_ENABLEDis off by default).