Changelog

oven-sh/bun · · 44 commits

Bun fixes fetch memory, redirects, and more

Major fetch and stream memory improvements landed alongside several notable correctness and crash fixes across networking, SQL, TLS, and FFI.

Fetch bodies now stream with much lower peak memory (robobun5f65d37)

Buffered consumers like .arrayBuffer(), .bytes(), and .text() now avoid the old over-allocation pattern and the streaming path can hand response bytes through as borrowed slices. The change should materially cut RSS for large fetches and concurrent streaming workloads, especially when bodies are read incrementally.

fetch(..., { redirect: 'error' }) now only rejects real redirect codes (robobunb7fef25)

Bun was treating every 300..399 status as a redirect, so responses like 300, 304, 305, and 306 were incorrectly rejected. This brings redirect handling in line with the WHATWG spec and avoids breaking callers that need to observe non-redirect 3xx responses.

Response.clone() no longer multiplies stream chunk copies (robobun5b7c3ca)

Stream tee branches now share chunk references instead of deep-copying each chunk through structured clone per branch. That removes a major memory blow-up for clone chains and makes cloned streamed bodies far cheaper to retain.

bytes() / arrayBuffer() single-chunk fast paths now copy safely (robobune03e8cf)

The single-chunk consumer fast path used to return the producer’s backing storage by identity, which could alias or transfer the live source buffer. This fixes the correctness hole by copying binary chunks even in the one-chunk case.

fs.watch(..., { recursive: true }) now reports subtree watch failures (robobun9ad23e2)

On Linux, failures from inotify_add_watch on nested directories were previously swallowed, leaving partially-watched trees with no error signal. The watcher now surfaces those failures as 'error' events so applications can detect incomplete coverage.

fs.statfs no longer truncates large filesystems on non-bigint paths (robobunceddc66)

The non-बigint statfs implementation was storing fields in i32, which overflowed on larger volumes and returned negative counts. Large filesystems now report correct values instead of wrapping.

FFI toBuffer() stops freeing caller-owned memory (robobun45ccba4)

bun:ffi now borrows caller-owned bytes unless a finalizer is provided, avoiding accidental GC-time frees of foreign memory. This closes a serious use-after-free / bad-free footgun for native integrations.

TLS session helpers now return resume-capable tickets on TLS 1.3 (60bee4f)

getSession() and getTLSTicket() now return the ticket-bearing session on TLS 1.3 connections, matching Node’s behavior. That restores session resumption for callers that cache the accessor result.

Bun.serve preserves handler Content-Length on 304 responses (robobun37a7767)

304 responses previously had their handler-supplied length replaced with 0, which is not valid framing for this status. Bun now forwards the handler value correctly and avoids synthesizing an invalid length.

server.stop(true) no longer crashes when a close handler closes a sibling (robobun529adec)

The uSockets teardown walk has been hardened against handlers that close other sockets during shutdown, eliminating a use-after-free in bursty TLS teardown scenarios. This is a real stability fix for concurrent connection shutdown paths.

SQL result rows no longer silently drop columns on structure mismatch (robobun997739f)

The row builder now asserts the structure/column offset invariant instead of skipping writes when it fails. That turns a silent data loss bug into an explicit failure and protects row shape integrity.

Other misc changes