This release fixes 119 issues (addressing 412 👍).
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
pnpm-lock.yaml
in bun install
bun install
now automatically migrates pnpm-lock.yaml
and pnpm-workspace.yaml
files to a bun.lock
, preserving resolved dependency versions. This includes support for pnpm workspaces and catalog:
dependencies.
Switch from pnpm install
to bun install
with a single command:
# In a pnpm project
bun install
bun install
is designed to work with existing Node.js projects. That means you can leverage bun install
's incredible performance while still using Node.js as your runtime.
If your project uses pnpm-workspace.yaml
, your package.json is updated to include "workspaces": ["<each package-name>"]
.
Thanks to @dylan-conway for implementing this!
Filter optional dependencies with --cpu
and --os
flags
You can now control which platform-specific optionalDependencies
are installed using the new --cpu
and --os
flags in bun install
. This is useful when you're installing dependencies for a different target environment, such as in a Docker container or a CI/CD pipeline.
You can provide multiple values to install dependencies for several targets at once.
# Install optional dependencies for a Linux ARM64 target
bun install --os linux --cpu arm64
# Install for both macOS and Linux on x64
bun install --os darwin --os linux --cpu x64
# Install for all supported platforms
bun install --os '*' --cpu '*'
Bun's Redis client now supports Pub/Sub
Bun's built-in RedisClient
now supports the Publish/Subscribe (Pub/Sub) messaging pattern. You can use the new .subscribe()
method to listen for messages on specific channels and the .publish()
method to send messages.
This enables real-time, event-driven communication patterns directly within your Bun applications.
import { RedisClient } from "bun";
const subscriber = new RedisClient("redis://localhost:6379");
await subscriber.connect();
await subscriber.subscribe("my-channel", (message, channel) => {
console.log(`Received message: "${message}" from channel: "${channel}"`);
// Received message: "Hello from Bun!" from channel: "my-channel"
});
import { RedisClient } from "bun";
const publisher = new RedisClient("redis://localhost:6379");
await publisher.connect();
// After a short delay to ensure the subscriber is ready
setTimeout(() => {
publisher.publish("my-channel", "Hello from Bun!");
}, 100);
Thanks to @markovejnovic for the contribution
Concurrent bun test
bun test
now supports running multiple async
tests concurrently within the same file using test.concurrent
. This can significantly speed up test suites that are I/O-bound, such as those making network requests or interacting with a database.
import { test, expect } from "bun:test";
// These three tests will run in parallel.
// The total execution time will be ~1 second, not 3 seconds.
test.concurrent("sends a request to server 1", async () => {
const response = await fetch("https://example.com/server-1");
expect(response.status).toBe(200);
});
test.concurrent("sends a request to server 2", async () => {
const response = await fetch("https://example.com/server-2");
expect(response.status).toBe(200);
});
// Chain with .each, .only, or other modifiers:
test.concurrent.each([
"https://example.com/server-4",
"https://example.com/server-5",
"https://example.com/server-6",
])("sends a request to server %s", async (url) => {
const response = await fetch(url);
expect(response.status).toBe(200);
});
Run groups of tests concurrently with describe.concurrent
.
import { describe, test, expect } from "bun:test";
describe.concurrent("server tests", () => {
test("sends a request to server 1", async () => {
const response = await fetch("https://example.com/server-1");
expect(response.status).toBe(200);
});
});
test("serial test", () => {
expect(1 + 1).toBe(2);
});
By default, a maximum of 20 tests will run concurrently. You can change this with the --max-concurrency
flag.
concurrentTestGlob
to make specific files run concurrently
To make specific files run concurrently, you can use the concurrentTestGlob
option in bunfig.toml
.
[test]
concurrentTestGlob = "**/integration/**/*.test.ts"
# You can also provide an array of patterns.
# concurrentTestGlob = [
# "**/integration/**/*.test.ts",
# "**/*-concurrent.test.ts",
# ]
When using concurrentTestGlob
, all tests in the files matching the glob will run concurrently.
test.serial
to make specific tests sequential
When you use describe.concurrent
, --concurrent
, or concurrentTestGlob
, you might still want to leave some tests sequential. You can do this by using the new test.serial
modifier.
import { test, expect } from "bun:test";
describe.concurrent("concurrent tests", () => {
test("async test", async () => {
await fetch("https://example.com/server-1");
expect(1 + 1).toBe(2);
});
test("async test #2", async () => {
await fetch("https://example.com/server-2");
expect(1 + 1).toBe(2);
});
test.serial("serial test", () => {
expect(1 + 1).toBe(2);
});
});
Run tests in a random order with --randomize
Concurrent tests sometimes expose unexpected test dependencies on execution order or shared state. You can use the --randomize
flag to run tests in a random order to make it easier to find these dependencies.
When you use --randomize
, Bun will output the seed for that specific run. To reproduce the exact same test order for debugging, you can use the --seed
flag with the printed value. Using --seed
automatically enables randomization.
# Run tests in a random order
bun test --randomize
# The seed is printed in the test summary
# ... test output ...
# --seed=12345
# 2 pass
# 8 fail
# Ran 10 tests across 2 files. [50.00ms]
# Reproduce the same run order using the seed
bun test --seed 12345
bun test
now supports chaining qualifiers
You can now chain qualifiers like .failing
, .skip
, .only
, and .each
on test
and describe
. Previously, this would result in an error.
import { test, expect } from "bun:test";
// This test is expected to fail, and it runs for each item in the array.
test.failing.each([1, 2, 3])("each %i", (i) => {
if (i > 0) {
throw new Error("This test is expected to fail.");
}
});
Stricter bun test
in CI environments
To prevent accidental commits, bun test
will now throw an error in CI environments (where CI=true
) in two new scenarios:
- If a test file contains
test.only()
. - If a snapshot test (
.toMatchSnapshot()
or.toMatchInlineSnapshot()
) tries to create a new snapshot without the--update-snapshots
flag.
This helps prevent temporarily focused tests or unintentional snapshot changes from being merged. To disable this behavior, you can set the environment variable CI=false
.
Test execution order improvements
The test runner's execution logic has been rewritten for improved reliability and predictability. This resolves a large number of issues where describe
blocks and hooks (beforeAll
, afterAll
, etc.) would execute in a slightly unexpected order. The new behavior is more consistent with test runners like Vitest.
Concurrent test limitations
expect.assertions()
andexpect.hasAssertions()
are not supported when usingtest.concurrent
ordescribe.concurrent
.toMatchSnapshot()
is not supported, buttoMatchInlineSnapshot()
is.beforeAll
andafterAll
hooks are not executed concurrently.
Thanks to @pfgithub for all of these big improvements to bun test
!
bun feedback
In the next version of Bun
— Jarred Sumner (@jarredsumner) September 16, 2025
bun feedback <files or text> makes it easier to send feedback about Bun to the Bun team pic.twitter.com/CkJAKwllzU
Node.js compatibility improvements
node:http
http.createServer
now supports the CONNECT
method. This allows for creating HTTP proxies with Bun.
node:dns
dns.resolve
's callback now matches Node.js by no longer passing an extra hostname
argument. dns.promises.resolve
now correctly returns an array of strings instead of objects for A/AAAA records.
node:worker_threads
A bug where MessagePort
communication would fail after being transferred to a Worker
when using port.on('message', ...)
or port.addEventListener('message', ...)
. This was caused by a different between Web Workers and worker_threads
.
node:crypto
A hypothetical crash in crypto.createSign().sign()
when using an Elliptic Curve (EC) key in JWK format with dsaEncoding: 'ieee-p1363'
has been fixed.
node:http2
A memory leak when closing sockets has been fixed.
node:net
A handle leak in net.connect()
that caused memory usage to grow when making many connections has been resolved.
node:tty
On Windows, TTY raw mode (process.stdin.setRawMode(true)
) now correctly handles terminal VT control sequences, improving compatibility with Node.js and enabling features like bracketed paste mode.
Use system's trusted certificates with --use-system-ca
Bun can now be configured to use the operating system's trusted root certificates for establishing TLS connections, in addition to the built-in Mozilla CA store. This is useful in corporate environments with custom Certificate Authorities (CAs) or for trusting locally installed self-signed certificates.
To enable this functionality, either use the new --use-system-ca
command-line flag or set the NODE_USE_SYSTEM_CA=1
environment variable, which aligns Bun with Node.js's behavior.
# This command will use the OS's trusted CAs to validate the certificate
# for "internal.service.corp".
bun run --use-system-ca index.js
# index.js
const response = await fetch("https://internal.service.corp");
console.log(response.status);
process.report.getReport()
is now implemented on Windows
The Node.js-compatible API process.report.getReport()
is now fully implemented on Windows. Previously, this would throw a "Not implemented" error. This function generates a comprehensive diagnostic report about the current process, including system information, JavaScript heap statistics, stack traces, and loaded shared libraries.
This change improves compatibility with tools that rely on this API for diagnostics or environment detection.
// On Windows, this now returns a detailed report object.
const report = process.report.getReport();
console.log(report.header.osVersion); // e.g., "Windows 11 Pro"
console.log(report.header.cpus.length > 0); // true
console.log(report.javascript.heap.heapSpaces.length > 0); // true
Codesigning for Windows in bun build --compile
bun build --compile
can create standalone executables from a JavaScript or TypeScript entrypoint. On Windows, these executables are often based on a pre-signed bun.exe
binary. Previously, when Bun embedded the application code and assets, it would invalidate this original signature, preventing developers from applying their own code signing certificate.
This release introduces automatic "Authenticode" signature stripping. When you create an executable, Bun now removes the original signature, allowing you to sign the compiled binary with your own certificate using standard Windows tools like signtool.exe
. This is essential for distributing trusted applications to Windows users.
# 1. Compile your application into a standalone executable
bun build ./index.ts --compile --outfile my-app.exe
# 2. Sign the resulting executable with your own certificate
signtool.exe sign /f MyCert.pfx /p MyPassword my-app.exe
Bun.build
gets a new jsx
configuration object
Configuration for JSX transforms in Bun.build
is now centralized in a new jsx
object. This includes options previously configured via tsconfig.json
, like jsxFactory
, jsxFragment
, and jsxImportSource
.
await Bun.build({
entrypoints: ["./index.jsx"],
outdir: "./dist",
jsx: {
runtime: "automatic", // "automatic" or "classic"
importSource: "preact", // defaults to "react"
factory: "h", // defaults to "React.createElement"
fragment: "Fragment", // defaults to "React.Fragment"
development: false, // use `jsx-dev` runtime, defaults to `false`
sideEffects: false,
},
});
sql.array
helper in Bun.SQL
The sql.array
helper in Bun.SQL makes it easy to work with PostgreSQL array types. You can insert arrays into array columns and specify the PostgreSQL data type for proper casting.
import { sql } from "bun";
// Insert an array of text values
await sql`
INSERT INTO users (name, roles)
VALUES (${"Alice"}, ${sql.array(["admin", "user"], "TEXT")})
`;
// Update with array values using sql object notation
await sql`
UPDATE users
SET ${sql({
name: "Bob",
roles: sql.array(["moderator", "user"], "TEXT"),
})}
WHERE id = ${userId}
`;
// Works with JSON/JSONB arrays
const jsonData = await sql`
SELECT ${sql.array([{ a: 1 }, { b: 2 }], "JSONB")} as data
`;
// Supports various PostgreSQL types
await sql`SELECT ${sql.array([1, 2, 3], "INTEGER")} as numbers`;
await sql`SELECT ${sql.array([true, false], "BOOLEAN")} as flags`;
await sql`SELECT ${sql.array([new Date()], "TIMESTAMP")} as dates`;
The sql.array
helper supports all major PostgreSQL array types including TEXT
, INTEGER
, BIGINT
, BOOLEAN
, JSON
, JSONB
, TIMESTAMP
, UUID
, INET
, and many more.
Thanks to @cirospaciari for the contribution!
Top level await improvements
Bun's bundler now has improved support for top-level await.
When there are cylical dependencies using top-level await, Bun will now wrap modules in await Promise.all
to ensure they are all loaded. We've also fixed a couple edgecases that could cause the async
keyword to be missing from bundled modules in certain cases.
Thanks to @dylan-conway for implementing this!
Bundler & dev-server bugfixes:
- Improved: syntax error messages for invalid escape sequences, like
\"
, outside of string literals. - Fixed: A bug causing sourcemap line numbers to be off-by-one or more in Chrome DevTools
- Fixed: A regression in the minifier where
new Array()
with a ternary expression would be bundled incorrectly has been fixed. - Fixed:
bun build
producing output that would hang indefinitely when bundling code with cyclic asynchronous module dependencies. - Fixed: a crash that occurred when a macro returned a complex or deeply-nested object or array.
bun install bugfixes:
- Fixed: An integer overflow when parsing package versions, which could cause
bun install
to crash or select the wrong version of a package. This affected packages like@google/gemini-cli@nightly
and@scratch/paper
that use large numbers in their version strings. - Fixed a bug where
bun install
could fail with anETXTBUSY
(Text file busy) error when linking package binaries. - Fixed a crash on Windows when running
bun outdated
orbun install
caused by a race condition.
bun test bugfixes:
- Fixed: An uncaught promise rejection in an
async
test no longer causes the test runner to hang. - Fixed:
test()
andafterAll()
are no longer allowed inside anothertest()
callback. Previously, innertest
callbacks were simply ignored. Bun will now throw an error instead of silently failing or producing unexpected behavior. - Fixed: When
test.only
is nested insidedescribe.only
, only the innermost.only
tests are executed. - Fixed: When using
describe.only
,beforeAll
hooks indescribe
blocks not marked with.only
will be correctly skipped. - Fixed: A failure in an
async beforeEach
hook now correctly prevents the corresponding test from running. - Fixed: Test hooks like
beforeAll
andbeforeEach
now support a timeout option and will fail if they exceed the specified duration. - Fixed: An
afterAll
hook will now run even if a correspondingbeforeAll
hook fails, which aligns with Jest's behavior and is useful for cleanup tasks. - Fixed: Throwing an error in a
beforeAll
hook loaded via--preload
now correctly halts test execution across all files. - Fixed: When an error is thrown inside a
describe
block, any nesteddescribe
blocks are now correctly skipped. - Fixed: An issue where
describe.todo()
was incorrectly executed when nested insidedescribe.only()
. - Fixed: A passing test inside a
describe.todo()
block is now handled correctly. - Fixed: An error in an async
beforeEach
hook is now reported correctly, instead of being masked as an "unhandled error between tests". - Fixed: An exception when using custom matchers from libraries like
jest-dom
that rely onthis.utils.RECEIVED_COLOR
andthis.utils.EXPECTED_COLOR
. Additionally,vi.resetAllMocks
,vi.useFakeTimers
, andvi.useRealTimers
are now stubbed until we finish implementing them. - Fixed:
bun test
now shows clearer error messages and help text for the--reporter
and--coverage-reporter
flags. - Fixed: A bug where
bun test
would not correctly load test files when using a.
in the path.
Bug fixes and reliability improvements
- Fixed:
YAML.parse()
now throws aSyntaxError
for invalid input, matching the behavior ofJSON.parse()
. - Fixed a bug where
fetch()
would only support single-framezstd
-compressed responses sent withTransfer-Encoding: chunked
. Bun's decompressor now correctly handles multi-framezstd
streams, ensuring the full response body is received. - Fixed: A bug where
fetch()
with anAbortSignal
would not abort if the signal was triggered while the underlying socket was connecting (only after it connected or failed to connect). This particularly affectedAbortSignal.timeout()
when making requests to unresponsive servers. - Fixed: A bug when console.logging an
Error
object could display a truncated stack trace. - Fixed a bug preventing
Bun.redis
from connecting to Redis/Valkey servers over TLS. This affected connections usingrediss://
or thetls: true
option. - Fixed: infinite loop when logging an
Error
object with a circular reference, likeerror.stack = error
or a circularerror.cause
chain. - Fixed a rare crash in
Bun.serve
that could occur during garbage collection. - Fixed a crash that occurred when a
Bun.plugin
'sonResolve
handler returnedundefined
ornull
. - Fixed an assertion failure on Windows caused by an incorrect file path.
- Fixed: A bug in
Bun.sql
'spostgres
driver whereNUMERIC
values with many digits were parsed incorrectly has been resolved. - Fixed: The
install.sh
script now prioritizes~/.bash_profile
over~/.bashrc
when updating thePATH
, aligning with standard Bash practices. - Internal: LeakSanitizer to our CI pipeline to automatically detect native memory leaks.
- Fixed: A crash that could occur when a UDP socket is active while the Bun process is exiting.
- Fixed: A very rare race condition that could cause
fetch()
to crash when handling many simultaneous redirects while anAbortSignal
was active. - Fixed: A stability issue in
Bun.serve
impacting large request bodies - Fixed:
BUN_CONFIG_VERBOSE_FETCH=curl
now prints the request body forfetch
requests withContent-Type: application/x-www-form-urlencoded
. - Fixed a crash when a large number of command-line arguments were passed to the current process and process.argv was accessed.
- Fixed: The current working directory in stack traces is now dimmed, improving readability and helping to identify local files.
- Fixed:
Bun.SQL
's MySQL driver did not work on Windows. - Upgraded libuv to v1.51.0, improving I/O reliability
- Fixed: Bun's browser error modal is now 250 KB smaller, making it faster to load.
- Fixed an issue where
npm install bun
would fail on Alpine Linux forarm64
.