cd /news/developer-tools/bun-v1-2-23 · home topics developer-tools article
[ARTICLE · art-9978] src=bun.com ↗ pub= topic=developer-tools verified=true sentiment=↑ positive

Bun v1.2.23

Bun v1.2.23 fixes 119 issues and introduces several new features. Key additions include automatic migration from pnpm-lock.yaml to bun.lock, new `--cpu` and `--os` flags for filtering optional dependencies, built-in Redis Pub/Sub support, and concurrent test execution with `test.concurrent`.

read14 min views16 publishedSep 28, 2025

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:

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

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

bun install --os linux --cpu arm64

bun install --os darwin --os linux --cpu x64

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"
});
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 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.

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"

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

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

bun test --randomize

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.

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

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

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

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

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

bun run --use-system-ca index.js
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.

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

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.

bun build ./index.ts --compile --outfile my-app.exe

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.

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

.

── more in #developer-tools 4 stories · sorted by recency
── more on @bun 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/bun-v1-2-23] indexed:0 read:14min 2025-09-28 ·