# Bun v1.2.23

> Source: <https://bun.com/blog/bun-v1.2.23>
> Published: 2025-09-28 10:11:00+00:00

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`

`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 ](#filter-optional-dependencies-with-cpu-and-os-flags)`--cpu`

and `--os`

flags

`--cpu`

and `--os`

flagsYou 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-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.

``` js
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"
});
js
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 ](#concurrent-bun-test)`bun test`

`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.

``` js
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`

.

``` js
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.

``` js
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 ](#run-tests-in-a-random-order-with-randomize)`--randomize`

`--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

`bun test`

now supports chaining qualifiersYou can now chain qualifiers like `.failing`

, `.skip`

, `.only`

, and `.each`

on `test`

and `describe`

. Previously, this would result in an error.

``` js
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 ](#stricter-bun-test-in-ci-environments)`bun test`

in CI environments

`bun test`

in CI environmentsTo 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](#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](#concurrent-test-limitations)

`expect.assertions()`

and`expect.hasAssertions()`

are not supported when using`test.concurrent`

or`describe.concurrent`

.`toMatchSnapshot()`

is not supported, but`toMatchInlineSnapshot()`

is.`beforeAll`

and`afterAll`

hooks are not executed concurrently.

Thanks to @pfgithub for all of these big improvements to `bun test`

!

`bun feedback`

`bun feedback`

In the next version of Bun

— Jarred Sumner (@jarredsumner)

bun feedback <files or text> makes it easier to send feedback about Bun to the Bun team[pic.twitter.com/CkJAKwllzU][September 16, 2025]

[Node.js compatibility improvements](#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-s-trusted-certificates-with-use-system-ca)`--use-system-ca`

`--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
js
# index.js
const response = await fetch("https://internal.service.corp");
console.log(response.status);
```

`process.report.getReport()`

is now implemented on Windows

`process.report.getReport()`

is now implemented on WindowsThe 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.

``` js
// 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 ](#codesigning-for-windows-in-bun-build-compile)`bun build --compile`

`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

`Bun.build`

gets a new `jsx`

configuration objectConfiguration 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

`sql.array`

helper in Bun.SQLThe `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.

``` js
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](#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:](#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:](#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 an`ETXTBUSY`

(Text file busy) error when linking package binaries. - Fixed a crash on Windows when running
`bun outdated`

or`bun install`

caused by a race condition.

[bun test bugfixes:](#bun-test-bugfixes)

- Fixed: An uncaught promise rejection in an
`async`

test no longer causes the test runner to hang. - Fixed:
`test()`

and`afterAll()`

are no longer allowed inside another`test()`

callback. Previously, inner`test`

callbacks were simply ignored. Bun will now throw an error instead of silently failing or producing unexpected behavior. - Fixed: When
`test.only`

is nested inside`describe.only`

, only the innermost`.only`

tests are executed. - Fixed: When using
`describe.only`

,`beforeAll`

hooks in`describe`

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`

and`beforeEach`

now support a timeout option and will fail if they exceed the specified duration. - Fixed: An
`afterAll`

hook will now run even if a corresponding`beforeAll`

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 nested`describe`

blocks are now correctly skipped. - Fixed: An issue where
`describe.todo()`

was incorrectly executed when nested inside`describe.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 on`this.utils.RECEIVED_COLOR`

and`this.utils.EXPECTED_COLOR`

. Additionally,`vi.resetAllMocks`

,`vi.useFakeTimers`

, and`vi.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](#bug-fixes-and-reliability-improvements)

- Fixed:
`YAML.parse()`

now throws a`SyntaxError`

for invalid input, matching the behavior of`JSON.parse()`

. - Fixed a bug where
`fetch()`

would only support single-frame`zstd`

-compressed responses sent with`Transfer-Encoding: chunked`

. Bun's decompressor now correctly handles multi-frame`zstd`

streams, ensuring the full response body is received. - Fixed: A bug where
`fetch()`

with an`AbortSignal`

would not abort if the signal was triggered while the underlying socket was connecting (only after it connected or failed to connect). This particularly affected`AbortSignal.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 using`rediss://`

or the`tls: true`

option. - Fixed: infinite loop when logging an
`Error`

object with a circular reference, like`error.stack = error`

or a circular`error.cause`

chain. - Fixed a rare crash in
`Bun.serve`

that could occur during garbage collection. - Fixed a crash that occurred when a
`Bun.plugin`

's`onResolve`

handler returned`undefined`

or`null`

. - Fixed an assertion failure on Windows caused by an incorrect file path.
- Fixed: A bug in
`Bun.sql`

's`postgres`

driver where`NUMERIC`

values with many digits were parsed incorrectly has been resolved. - Fixed: The
`install.sh`

script now prioritizes`~/.bash_profile`

over`~/.bashrc`

when updating the`PATH`

, 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 an`AbortSignal`

was active. - Fixed: A stability issue in
`Bun.serve`

impacting large request bodies - Fixed:
`BUN_CONFIG_VERBOSE_FETCH=curl`

now prints the request body for`fetch`

requests with`Content-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 for`arm64`

.
