This release fixes 119 issues (addressing 412 👍).
To install Bun
curl -fsSL https://bun.sh/install | bashnpm install -g bunpowershell -c "irm bun.sh/install.ps1|iex"scoop install bunbrew tap oven-sh/bunbrew install bundocker pull oven/bundocker run --rm --init --ulimit memlock=-1:-1 oven/bunTo upgrade Bun
bun upgradepnpm-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 projectbun installbun 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 orderbun 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 seedbun test --seed 12345bun 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-snapshotsflag.
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.concurrentordescribe.concurrent.toMatchSnapshot()is not supported, buttoMatchInlineSnapshot()is.beforeAllandafterAllhooks 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 buildproducing 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 installto crash or select the wrong version of a package. This affected packages like@google/gemini-cli@nightlyand@scratch/paperthat use large numbers in their version strings. - Fixed a bug where
bun installcould fail with anETXTBUSY(Text file busy) error when linking package binaries. - Fixed a crash on Windows when running
bun outdatedorbun installcaused by a race condition.
bun test bugfixes:
- Fixed: An uncaught promise rejection in an
asynctest no longer causes the test runner to hang. - Fixed:
test()andafterAll()are no longer allowed inside anothertest()callback. Previously, innertestcallbacks were simply ignored. Bun will now throw an error instead of silently failing or producing unexpected behavior. - Fixed: When
test.onlyis nested insidedescribe.only, only the innermost.onlytests are executed. - Fixed: When using
describe.only,beforeAllhooks indescribeblocks not marked with.onlywill be correctly skipped. - Fixed: A failure in an
async beforeEachhook now correctly prevents the corresponding test from running. - Fixed: Test hooks like
beforeAllandbeforeEachnow support a timeout option and will fail if they exceed the specified duration. - Fixed: An
afterAllhook will now run even if a correspondingbeforeAllhook fails, which aligns with Jest's behavior and is useful for cleanup tasks. - Fixed: Throwing an error in a
beforeAllhook loaded via--preloadnow correctly halts test execution across all files. - Fixed: When an error is thrown inside a
describeblock, any nesteddescribeblocks 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
beforeEachhook 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-domthat rely onthis.utils.RECEIVED_COLORandthis.utils.EXPECTED_COLOR. Additionally,vi.resetAllMocks,vi.useFakeTimers, andvi.useRealTimersare now stubbed until we finish implementing them. - Fixed:
bun testnow shows clearer error messages and help text for the--reporterand--coverage-reporterflags. - Fixed: A bug where
bun testwould not correctly load test files when using a.in the path.
Bug fixes and reliability improvements
- Fixed:
YAML.parse()now throws aSyntaxErrorfor 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-framezstdstreams, ensuring the full response body is received. - Fixed: A bug where
fetch()with anAbortSignalwould 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
Errorobject could display a truncated stack trace. - Fixed a bug preventing
Bun.redisfrom connecting to Redis/Valkey servers over TLS. This affected connections usingrediss://or thetls: trueoption. - Fixed: infinite loop when logging an
Errorobject with a circular reference, likeerror.stack = erroror a circularerror.causechain. - Fixed a rare crash in
Bun.servethat could occur during garbage collection. - Fixed a crash that occurred when a
Bun.plugin'sonResolvehandler returnedundefinedornull. - Fixed an assertion failure on Windows caused by an incorrect file path.
- Fixed: A bug in
Bun.sql'spostgresdriver whereNUMERICvalues with many digits were parsed incorrectly has been resolved. - Fixed: The
install.shscript now prioritizes~/.bash_profileover~/.bashrcwhen 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 anAbortSignalwas active. - Fixed: A stability issue in
Bun.serveimpacting large request bodies - Fixed:
BUN_CONFIG_VERBOSE_FETCH=curlnow prints the request body forfetchrequests 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 bunwould fail on Alpine Linux forarm64.