This release fixes 141 issues (addressing 429 👍) and includes many reliability improvements throughout the runtime, the bundler, and the dev server.
To install Bun
curl -fsSL https://bun.sh/install | bash
npm install -g bun
powershell -c "irm bun.sh/install.ps1|iex"
scoop install bun
brew tap oven-sh/bun
brew install bun
docker pull oven/bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun
To upgrade Bun
bun upgrade
Reduced idle CPU usage
Over-scheduling the garbage collector when idling had a serious impact on idle CPU usage.
In the next version of Bun
— Jarred Sumner (@jarredsumner) August 3, 2025
Idle CPU time is reduced pic.twitter.com/MbBvbWgMew
This change had a neutral impact on memory usage.
Automatic yarn.lock
migration
bun install
will now automatically migrate your yarn.lock
(v1) file to a bun.lock
file.
In the next version of Bun
— Bun (@bunjavascript) July 29, 2025
bun install now automatically migrates from yarn.lock -> bun.lock, preserving resolved dependency versions pic.twitter.com/6fq80Xz3Dy
Thanks to @RiskyMH for the contribution
40x faster AbortSignal.timeout
We rewrote AbortSignal.timeout
to use the same underlying implementation as setTimeout
. This makes it 40x faster.

Improved bun:test
diffing
The diffing output in bun:test
has been redesigned for improved readability.
Whitespace differences are highlighted.

Several edgecases involving non-ascii characters and numerous other bugs have been fixed.
Thanks to @pfgithub for the contribution!
New bun:test
matchers for return values
Three new bun:test
matchers have been added for asserting the return values of mock functions: toHaveReturnedWith
, toHaveLastReturnedWith
, and toHaveNthReturnedWith
. These matchers use deep equality, similar to toEqual()
, and support asymmetric matchers.
toHaveReturnedWith()
: Checks if the mock function has returned a specific value in any of its calls.toHaveLastReturnedWith()
: Checks the return value of the most recent call.toHaveNthReturnedWith()
: Checks the return value of a specific call (1-indexed).
import { test, expect, mock } from "bun:test";
test("toHaveReturnedWith", () => {
const returnsAnObject = mock(() => ({ a: 1 }));
returnsAnObject();
expect(returnsAnObject).toHaveReturnedWith({ a: 1 });
});
test("toHaveLastReturnedWith", () => {
const returnsAString = mock((i) => `call ${i}`);
returnsAString(1);
returnsAString(2);
expect(returnsAString).toHaveLastReturnedWith("call 2");
});
test("toHaveNthReturnedWith", () => {
const returnsANumber = mock((i) => i * 10);
returnsANumber(1);
returnsANumber(2);
returnsANumber(3);
expect(returnsANumber).toHaveNthReturnedWith(2, 20);
});
Thanks to @dylan-conway for the contribution!
Test your types with expectTypeOf
bun:test
now includes expectTypeOf
for asserting TypeScript types, with an API compatible with Vitest.
These assertions are no-ops at runtime; they are checked by the TypeScript compiler. To verify your type tests, run bunx tsc --noEmit
.
import { expectTypeOf, test } from "bun:test";
test("type-level tests", () => {
// Basic type assertions
expectTypeOf("hello").toBeString();
expectTypeOf(123).toBeNumber();
// Check object shapes
expectTypeOf({ a: 1, b: "2" }).toMatchObjectType<{ a: number }>();
// Assert function parameter and return types
function add(a: number, b: number): number {
return a + b;
}
expectTypeOf(add).parameters.toEqualTypeOf<[number, number]>();
expectTypeOf(add).returns.toBeNumber();
});
Thanks to @pfgithub for the contribution
mock.clearAllMocks()
for bun:test
The bun:test
module now implements mock.clearAllMocks()
, a function to reset the state of all mocked functions. This function clears the .mock.calls
and .mock.results
properties of all mocks, but importantly, it does not restore their original implementations.
This is useful for resetting mock states between tests, for instance in a global setup file, without needing to manually track and clear each individual mock.
import { test, mock, expect } from "bun:test";
const random = mock(() => Math.random());
test("clearing all mocks", () => {
random();
expect(random).toHaveBeenCalledTimes(1);
// Reset the state of all mocks
mock.clearAllMocks();
expect(random).toHaveBeenCalledTimes(0);
// The mock implementation is preserved
expect(typeof random()).toBe("number");
expect(random).toHaveBeenCalledTimes(1);
});
Thanks to @pfgithub for the contribution
bun outdated
and bun update
now supports --recursive
bun outdated
and bun update --interactive
now have improved support for workspaces, making it easier to manage dependencies in monorepos.
You can now run these commands across all workspaces using the -r
or --recursive
flag.
# See outdated packages in all workspaces
bun outdated --recursive
# Interactively update packages in all workspaces
bun update -i -r
When running bun update -i
in a monorepo, a new "Workspace" column will be displayed, showing which workspace a dependency belongs to:
bun update -i --recursive
dependencies Current Target Latest Workspace
❯ □ @types/node 24.1.0 24.2.1 24.2.1 bun-types
Additionally, the --filter
flag is now supported in bun update -i
, allowing you to scope updates to specific workspaces.
# Interactively update dependencies only in the "my-app" workspace
bun update -i --filter="my-app"
This also fixes issues where bun update
would not correctly handle catalog dependencies. Additionally bun outdated
and bun update -i
shows which packages use the catelog in the workspace column.
Thanks to @RiskyMH for the contribution
Automatic ETag
and If-None-Match
in static routes of Bun.serve
Bun.serve
now automatically generates ETag
headers for static routes defined in the static
option. When a client sends an If-None-Match
header, Bun compares the ETag and sends a 304 Not Modified
response if the content is unchanged. This improves caching efficiency and saves bandwidth, with no code changes required to enable it.
const server = Bun.serve({
port: 0,
routes: {
"/latest.json": Response.json({ ...myBigObject }),
},
});
const url = new URL("/latest.json", server.url);
const etag = await fetch(url).then((res) => res.headers.get("etag"));
const { status } = await fetch(url, {
headers: {
"If-None-Match": etag,
},
});
console.log({ status, etag });
Windows long path support
Bun now consistently supports file paths longer than 260 characters on Windows. This is enabled via an application manifest, removing a common source of file-related errors in projects with deep directory structures or long file names.
import { mkdirSync, existsSync, rmSync } from "fs";
import { join } from "path";
// This path is longer than 260 characters
const longPath = join("C:\\", "a".repeat(270));
// This could've previously failed on Windows
mkdirSync(longPath, { recursive: true });
console.log(`Exists: ${existsSync(longPath)}`); // Exists: true
rmSync(longPath, { recursive: true, force: true });
Previously, supporting long file paths on Windows involved extremely complicated file path namespacing we internally added to most of our code. Now it's much simpler, since the Win32 API supports long paths natively.
WebAssembly.compileStreaming
and WebAssembly.instantiateStreaming
Bun now supports WebAssembly.compileStreaming()
and WebAssembly.instantiateStreaming()
. These APIs allow you to compile and instantiate a WebAssembly module directly from a streaming source, like the Response
from a fetch()
call.
This approach is more efficient than the non-streaming alternatives (WebAssembly.compile
and WebAssembly.instantiate
), as it avoids buffering the entire Wasm module into memory. Compilation can start as soon as the first bytes are received, reducing latency and memory usage.
// Bun will stream the response body directly to the Wasm compiler.
const { instance, module } = await WebAssembly.instantiateStreaming(
fetch("http://localhost:3000/add.wasm"),
);
// Use the instantiated module
console.log(instance.exports.add(2, 3)); // => 5
Thanks to @CountBleck for the contribution
Node.js compatibility improvements
- Fixed: A type error when assigning to
require.main
due to an outdated type definition. Themain
property is no longerreadonly
. - Fixed:
Invalid source map
errors were incorrectly logged when runningnext dev --turbopack
. - Fixed: cancelling an HTTP request, such as by rapidly refreshing a page in
next dev
, could cause anERR_STREAM_ALREADY_FINISHED
error - Fixed: a thread-safety issue when using Zstandard streams with the
zlib
module. - Fixed: a potential crash in
node:crypto
'sX509Certificate
when invalid input was provided. - Fixed: a hypothetical bug where an uncaught exception inside
process.nextTick
itself or a microtask would not be reported - Fixed: memory leak in
fs.mkdir
andfs.mkdirSync
with{ recursive: true }
has been resolved. - Fixed: missing
[Symbol.asyncIterator]
inprocess.stdout
andprocess.stderr
when it's a TTY or pipe. This bug impacted some users of Claude Code on Linux.
Bun shell fixes
- Fixed: a crash in
bun shell
when using pipelines with built-in commands that exit immediately - Fixed:
$.braces()
now supports patterns containing Unicode characters and more deeply nested expansions. - Fixed: Shell lexer would incorrectly display the
=
token as+
in error messages. - Fixed: a crash when parsing invalid syntax in certain cases.
Bundler bugfixes
- Fixed: a stack overflow in the JavaScript parser when parsing deeply nested expressions. This was resolved by refactoring the parser to use less stack space, preventing crashes with long chains of member accesses or function calls.
- Fixed: a bug where
bun build
would generate invalid code for cyclic imports containing a top-levelawait
dependency, causing a syntax error.
bun install bugfixes
- Fixed: a bug with
--linker=isolated
when running lifecycle scripts with paths that contain non-ASCII characters. - Fixed: a crash when a permissions error during installation of a dependency in
bun install
that could occur in non-interactive environments like GitHub Actions.
Frontend dev-server bugfixes
- Fixed: a crash in
--hot
mode when using vim-like swapfiles that could occur when a file's imports are changed or removed. - Fixed: a crash in the dev server with
--hot
enabled when deleting a file imported by multiple other files. - Fixed: a crash in the file watcher on Windows that could occur when many files changed at once, such as when switching git branches. This was caused by an index-out-of-bounds error when the number of file system events exceeded an internal buffer.
- Fixed: a crash that could occur when a client aborts an HTTP request.
- Fixed: a potential performance bottleneck in the dev server by improving internal buffer management for path resolution.
- Fixed a bug where
import.meta.url
was incorrect in the browser when using Hot Module Replacement (HMR). It now correctly useswindow.location.origin
instead ofbun://
.
bun:test bugfixes
- Fixed:
expect(...).toHaveBeenCalledWith()
and related mock function matchers now show a colorful diff when assertions fail, making it much easier to debug tests with complex object arguments. - Fixed:
beforeAll
hooks inbun test
would run fordescribe
blocks that did not contain any tests matching the current test filter. - Fixed: bug with
expect(() => { throw undefined }).toThrow(ErrorClass)
- Fixed:
jest.fn().mockRejectedValue()
would cause an unhandled rejection if the mocked function was never called. - Fixed:
beforeAll
hooks inbun test
would run fordescribe
blocks even when all tests within that block were filtered out. - Fixed:
bun test
would not correctly filter tests when a directory name was passed as an argument. - Fixed:
bun test
would display the warning for a passingtest.failing()
on the wrong line. - Fixed:
toIncludeRepeated
inbun:test
checked for at least the specified number of occurrences instead of the exact number, aligning Bun's behavior with Jest. - Fixed:
bun test
would fail when running multiple test files using thenode:test
module.
Windows bugfixes
- Fixed: an assertion failure on Windows when resolving an invalid file path.
- Fixed: an integer cast panic when reading large files on Windows
Runtime bugfixes
- Fixed: terminal syntax highlighter that would incorrectly add an extra closing brace
}
to error messages involving template literals. - Improved: Transfer-Encoding header validation with duplicate values. Thanks to Radman Siddiki for the report.
Bun.SQL
- Fixed: a crash in
Bun.SQL
that could occur when a database connection fails. - Fixed: an "index out of bounds" that could occur during large batch inserts.
- Fixed: a bug in
Bun.sql
client that could cause the process to hang indefinitely, particularly when queries were pending during shutdown.
File system and Bun.file
- Fixed: calling
.write()
,.writer()
,.unlink()
, or.delete()
on a read-onlyBlob
(one not created viaBun.file()
) would not throw an error. It now correctly throws an error stating thatBlob
s backed by bytes are read-only, matching the behavior ofBun.write()
.
Bun.which
and executable resolution
- Fixed:
Bun.which
on Windows failed to find executables in directory paths containing non-ASCII characters. This impacted lifecycle scripts in bun install as well.
Bun.resolve
and module resolution
- Fixed:
Bun.resolve()
andBun.resolveSync()
now consistently throwError
objects on failure. Previously, they could throw raw values, causing crashes intry...catch
blocks.
Bun.s3
- Fixed a crash that could occur in
Bun.s3.unlink()
when S3 credentials were not configured.
Response constructor and Web APIs
- Fixed: string coercion of
statusText
to theResponse
constructor - Fixed:
Response.redirect()
will now correctly throw aRangeError
when an invalid status code is provided.
Environment variables and configuration
- Fixed a bug where environment variables loaded from
.env
files would be truncated if they were longer than 4096 characters. This could also cause a crash.
Installation and platform-specific fixes
- Fixed a bug in the Windows install script (
install.ps1
) that incorrectly joined the local script'sPATH
environment variable by joining paths with spaces instead of semicolons.
Memory management and performance
- Bun's threadpool now releases memory more aggressively after 10 seconds of inactivity, reducing memory usage during idle periods.
- Optimized an internal synchronization primitive,
WaitGroup
, by replacing mutex locks with atomic operations. This can improve performance in scenarios with a high number of concurrent tasks.
TypeScript type improvements
- Fixed:
@types/bun
is now compatible with TypeScript 5.9, resolving a type conflict withArrayBuffer
that previously requiredskipLibCheck: true
intsconfig.json
. - In
bun:sqlite
, TypeScript types fordb.transaction()
have been improved to correctly infer the return type from the transaction callback. This allows you to return data from within a transaction in a type-safe way. The arguments passed to the transaction are also correctly typed.