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 upgradeAsync Stack Traces
Bun's stack traces now include asynchronous call frames, making it significantly easier to debug code using async/await. Previously, these frames were omitted, leading to incomplete stack traces. Now, you'll see the full asynchronous execution path leading to an error.
async function foo() {
  return await bar();
}
async function bar() {
  return await baz();
}
async function baz() {
  await 1; // ensure it's a real async function
  throw new Error("oops");
}
try {
  await foo();
} catch (e) {
  console.log(e);
}
This now outputs:
❯ bun async.js
 6 |   return await baz();
 7 | }
 8 |
 9 | async function baz() {
10 |   await 1; // ensure it's a real async function
11 |   throw new Error("oops");
                 ^
error: oops
      at baz (async.js:11:13)
      at async bar (async.js:6:16)
      at async foo (async.js:2:16)
Previously, it would output:
❯ bun-1.2.21 async.js
 6 |   return await baz();
 7 | }
 8 |
 9 | async function baz() {
10 |   await 1; // ensure it's a real async function
11 |   throw new Error("oops");
             ^
error: oops
      at baz (async.js:11:9)
Additionally, a bug that could cause stack traces printed to the console to be missing frames after accessing error.stack has been fixed.
And a bug with the error stack for Error subclases has been fixed.
In the next version of Bun & Safari
— Bun (@bunjavascript) September 1, 2025
Error subclasses show where the error was thrown as the 1st frame instead of the subclass' constructor, thanks to @__sosukesuzuki pic.twitter.com/NC8Ex9DC3K
Huge thanks to @sosukesuzuki for implementing this in JavaScriptCore!
240x faster postMessage and structuredClone for simple objects
A new fast path has been added for postMessage and structuredClone when dealing with "simple" objects. This optimization applies to plain JavaScript objects that contain only primitive values like strings, numbers, booleans, null, and undefined.
In the next version of Bun
— Jarred Sumner (@jarredsumner) August 31, 2025
postMessage(object) gets up to 200x faster for objects with strings or primitive values. pic.twitter.com/Z4Nrj4HtJL
Bun.YAML.stringify
In the next version of Bun
— Bun (@bunjavascript) August 27, 2025
Bun.YAML.stringify converts JavaScript objects into YAML pic.twitter.com/ioaSb07EPX
Support for interactive TTYs after stdin closes
A common pattern for TUI (Terminal User Interface) applications is to first process data piped from stdin, and then open /dev/tty to start an interactive session. This pattern was previously broken in Bun, causing an ESPIPE error when reading from the TTY stream.
This has been fixed by correctly handling file streams for character devices. Additionally, tty.ReadStream now supports .ref() and .unref() for parity with Node.js, allowing for better control over the event loop in TUI applications. This resolves a long-standing bug affecting many interactive CLI tools.
import { createReadStream } from "fs";
import { stdin, stdout } from "process";
// 1. Process any data piped from stdin
for await (const chunk of stdin) {
  stdout.write(`Piped data: ${chunk}`);
}
// 2. After stdin closes, open /dev/tty for interactive input
stdout.write(
  "stdin closed. Now accepting interactive input (type 'exit' to quit):\n"
);
const tty = createReadStream("/dev/tty");
for await (const chunk of tty) {
  const line = chunk.toString().trim();
  stdout.write(`You typed: "${line}"\n`);
  if (line === "exit") {
    tty.destroy();
  }
}
To run:
echo "Initial data" | bun script.js
Bun.SQL improvements
MySQL Adapter
In Bun v1.2.21, we introduced MySQL & SQLite adapters for Bun.SQL. This release includes several improvements to the MySQL adapter.
affectedRows and lastInsertRowid
affectedRows and lastInsertRowid properties are now returned by the MySQL driver. affectedRows contains the number of rows changed by the query, and lastInsertRowid contains the ID of the last inserted row. This brings the MySQL driver's API closer to Bun.SQL.
import { sql } from "bun";
// For INSERT queries
const insertResult = await sql`INSERT INTO users (name) VALUES ('John Doe');`;
console.log(insertResult.lastInsertRowid); // e.g., 1
console.log(insertResult.affectedRows); // 1
// For UPDATE queries
const updateResult =
  await sql`UPDATE users SET name = 'Jane Doe' WHERE id = 1;`;
console.log(updateResult.affectedRows); // 1
Better column type handling
Thanks to community feedback, we've made the following improvements to MySQL driver's column type handling:
| MySQL Type | JavaScript Type | Notes | 
|---|---|---|
| INT, TINYINT, MEDIUMINT | number | Within safe integer range | 
| BIT(1) | boolean | 
Previously:
- TINYINTcolumns were incorrectly parsed as booleans instead of numbers.
- BIT(1)columns were incorrectly parsed as numbers instead of booleans.
- BIT(N)columns (where N > 1) were incorrectly parsed as- Buffers instead of numbers, but only when the binary protocol was used.
TLS Support
Bun.SQL now supports connecting to MySQL databases over a TLS/SSL connection. This is done by passing a tls option to the Bun.SQL constructor, matching the behavior of the PostgreSQL adapter and other Bun APIs.
mysql_native_password authentication
Bun.SQL now correctly handles mysql_native_password authentication and authentication switching, improving compatibility with a wider range of MySQL servers.
PostgreSQL improvements
- Fixed: A bug in Bun.SQL's Postgres driver where an error in a pipelined query could cause the connection to disconnect.
- Fixed: In certain cases, Bun.SQL could resolve a PostgreSQL query's Promise before the server reported being ready.
- Fixed: Bun.SQL now correctly decodes TIMEandTIMETZcolumns from PostgreSQL when using the binary protocol.
Bundler & minifier
In the next version of Bun
— Bun (@bunjavascript) September 4, 2025
Bun's JavaScript transpiler & minifier gets slightly smarter pic.twitter.com/W84u1C9Q7a
Minifier optimizes new expressions for smaller bundles
Bun's JavaScript minifier now implements several new optimizations for built-in constructors, resulting in smaller bundle sizes.
When constructing built-in objects like Object, Array, or Error, the new keyword is often optional and has no effect on the runtime behavior. Bun now automatically removes the new keyword or replaces the entire expression with a more compact literal form, such as converting new Object() to {}.
This optimization applies to new Object(), new Array(), new Error() (and its subtypes), and new Function().
-const obj=new Object();
+const obj={};
-const arr=new Array(1, 2, 3);
+const arr=[1,2,3];
-const err=new Error("Something went wrong");
+const err=Error("Something went wrong");
Thanks to @dylan-conway for the contribution
typeof undefined checks are now minified
Bun's minifier now optimizes typeof checks for "undefined". This common optimization, also used by tools like esbuild, reduces bundle size by replacing string comparisons with shorter character comparisons. This change also improves dead code elimination for code guarded by these checks.
// Input
console.log(typeof x === "undefined");
console.log(typeof x !== "undefined");
// Minified output
console.log(typeof x > "u");
console.log(typeof x < "u");
Thanks to @Jarred-Sumner and @dylan-conway for this change
onEnd hook for bundler plugins
Bun.build now supports the onEnd hook in plugins, aligning its plugin API closer to esbuild. This hook is triggered after a build is complete—whether it succeeds or fails—and provides access to the BuildOutput object. This is useful for post-processing, cleanup, or sending notifications.
await Bun.build({
  entrypoints: ["./index.ts"],
  outdir: "./out",
  plugins: [
    {
      name: "onEnd example",
      setup(build) {
        build.onEnd((result) => {
          if (result.success) {
            console.log(
              `✅ Build succeeded with ${result.outputs.length} outputs`,
            );
          } else {
            console.error(`❌ Build failed with ${result.logs.length} errors`);
          }
        });
      },
    },
  ],
});
Thanks to @alii for the contribution
jsxSideEffects option
By default, Bun's bundler treats JSX as "pure", meaning it can be removed if its return value is unused. This process, known as dead code elimination, can cause problems when a component has side effects that need to be preserved.
A new option, jsxSideEffects, has been introduced to prevent this. When set to true, Bun will no longer mark JSX as pure, ensuring that components with side effects are always included in the final bundle.
You can enable this feature in your tsconfig.json or by using a command-line flag.
// tsconfig.json
{
  "compilerOptions": {
    "jsxSideEffects": true
  }
}
This ensures that code with side effects, like the example below, behaves as expected.
// component.jsx
let counter = 0;
function MyComponent() {
  counter++; // This side effect will now be preserved
  return <div>Hello</div>;
}
// index.jsx
<MyComponent />;
console.log(counter); // Reliably logs 1
Unused function and class names are now removed during minification
Bun's bundler now removes unused function and class expression names when minifying (--minify-syntax). This matches esbuild's behavior and results in smaller bundle sizes.
A new --keep-names flag has been added to preserve these names, which can be useful for debugging or for libraries that rely on Function.prototype.name.
// input.js
const myFunc = function myInternalName() {
  // "myInternalName" is not used anywhere
};
const myClass = class MyInternalClass {
  // "MyInternalClass" is not used anywhere
};
// After `bun build --minify ./input.js`:
//
// const myFunc = function() {};
// const myClass = class {};
// To preserve the names, use `bun build --minify --keep-names`
// or `keepNames: true` in your build configuration.
Thanks to @dylan-conway for the contribution
Monitor event loop delay with perf_hooks.monitorEventLoopDelay()
Bun now implements the perf_hooks.monitorEventLoopDelay() API for Node.js compatibility. This function creates and returns an IntervalHistogram object that samples the event loop delay in nanoseconds. You can use it to diagnose performance issues and understand your application's responsiveness.
import { monitorEventLoopDelay } from "perf_hooks";
const histogram = monitorEventLoopDelay({ resolution: 20 });
histogram.enable();
// Introduce a delay
await Bun.sleep(100);
histogram.disable();
console.log("Event Loop Delay (ns):");
console.log("Min:", histogram.min);
console.log("Max:", histogram.max);
console.log("Mean:", histogram.mean);
console.log("50th Percentile:", histogram.percentile(50));
console.log("99th Percentile:", histogram.percentile(99));
// Reset for next measurement
histogram.reset();
http.Server.prototype.closeIdleConnections() is now implemented
The server.closeIdleConnections() method from Node.js's http module is now implemented. This method immediately closes all sockets that are not currently handling a request, which is useful for gracefully shutting down an HTTP server without waiting for idle keep-alive connections to time out.
import { createServer } from "http";
const server = createServer((req, res) => {
  res.end("Hello, World!");
});
server.listen(3000, () => {
  console.log("Server listening on port 3000");
  // On a shutdown signal (e.g. SIGINT)
  process.on("SIGINT", () => {
    console.log("Closing server...");
    // Stop accepting new connections
    server.close((err) => {
      if (err) {
        console.error(err);
        process.exit(1);
      }
      console.log("Server closed.");
    });
    // Forcefully close any idle keep-alive connections
    // This allows the server.close() callback to fire sooner.
    server.closeIdleConnections();
  });
});
bun run now supports --workspaces
In the next version of Bun
— Bun (@bunjavascript) September 7, 2025
The --workspaces flag in bun run runs the package.json script in each workspace package pic.twitter.com/sXJFwhnx79
Thanks to @dylan-conway for the contribution
RFC 6455 compliant WebSocket client subprotocol negotiation
Bun's WebSocket client now correctly implements subprotocol negotiation as specified in RFC 6455. When you instantiate a new WebSocket() with an array of desired subprotocols, Bun will send them in the Sec-WebSocket-Protocol header.
The server can then select one of these protocols to use for the connection. Bun correctly validates the server's choice and makes the selected protocol available on the ws.protocol property. If the server responds with an invalid protocol, or doesn't select a protocol when one is required, the connection is now correctly rejected.
This fixes a long-standing compatibility issue with popular libraries like ws and obs-websocket-js.
// server (which already worked fine):
Bun.serve({
  port: 3000,
  fetch(req, server) {
    // Client is requesting "chat" and "superchat" protocols.
    // We'll select "chat".
    const success = server.upgrade(req, {
      headers: {
        "Sec-WebSocket-Protocol": "chat",
      },
    });
    return success
      ? undefined
      : new Response("Upgrade failed", { status: 500 });
  },
  websocket: {
    open(ws) {
      console.log(`Server: new connection with protocol "${ws.protocol}"`);
    },
  },
});
// Client code:
const ws = new WebSocket("ws://localhost:3000", ["chat", "superchat"]);
ws.onopen = () => {
  // `ws.protocol` is now correctly set to the protocol
  // selected by the server.
  // Before, this would be an empty string.
  console.log(`Client: connected with protocol "${ws.protocol}"`); // "chat"
  ws.close();
};
Override Host and other headers in new WebSocket()
You can now override special WebSocket headers like Host, Sec-WebSocket-Key, and Sec-WebSocket-Protocol when creating a new client-side WebSocket connection. This is done by passing a headers object to the WebSocket constructor.
This is useful for advanced use-cases like connecting through proxies that require a specific Host header, testing servers with specific keys, or implementing custom subprotocol negotiation. Bun will automatically handle generating required WebSocket headers if they are not provided.
// Bun now supports overriding special headers in the WebSocket client
const ws = new WebSocket("ws://localhost:8080", {
  headers: {
    "Host": "custom-host.example.com",
    "Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==", // Must be a valid base64-encoded 16-byte key
    "Sec-WebSocket-Protocol": "chat, superchat",
    // This already worked:
    "X-Custom-Header": "MyValue",
  },
});
Connect to specific Redis databases with Bun.RedisClient
Bun's built-in Redis client, Bun.RedisClient, now supports specifying a database number directly in the connection URL, aligning with the standard Redis URI scheme. You can append /db-number to your connection string to connect to a specific database without manually sending a SELECT command.
import { RedisClient } from "bun";
// Connect to database #2
const client = new RedisClient("redis://localhost:6379/2");
// Set a key in DB 2
await client.set("foo", "bar");
console.log(await client.get("foo")); // "bar"
// Connect to the default database #0
const defaultClient = new RedisClient("redis://localhost:6379/0");
// The key "foo" will not be found in DB 0
console.log(await defaultClient.get("foo")); // null
Thanks to @HeyItsBATMAN for the contribution
Bun.redis now supports hget()
Bun's built-in Redis client now supports the HGET command. This provides a more ergonomic and performant way to retrieve a single field from a Redis hash, returning the value directly instead of a single-element array like hmget. For single-field lookups, hget() is approximately 2x faster than hmget().
import { redis } from "bun";
// Old way: returns an array
const [value] = await redis.hmget("my-hash", "my-field");
// New way: returns the value directly
const value = await redis.hget("my-hash", "my-field");
Faster number handling in Bun's APIs
Bun's internal APIs that return numbers, such as fs.statSync(), performance.now(), and process.memoryUsage(), now use a more efficient number representation when the value is a whole number.
Previously, these values were always represented as double-precision floating-point numbers. Now, they can be represented as tagged 32-bit integers, which makes subsequent arithmetic operations on these values significantly faster in JavaScriptCore.
// Bun's APIs now return numbers in a more efficient format when possible.
// Operations like this can be faster.
const stats = fs.statSync(file);
const size = stats.size; // This is now a tagged integer if it fits.
Node.js compatibility improvements:
- Fixed: A RangeErrorexception inchild_process.spawnSyncwhen thestdiooption was configured withprocess.stderrorprocess.stdout. This unblocks usage of tools like the AWS CDK.
- Fixed: A bug where socket.write()fromnode:netwould incorrectly throw an exception when passed aUint8Array. Now it allows passingUint8Arrays, matching Node.js behavior.
- Fixed: A bug where crypto.verify()would throw an error when anullorundefinedalgorithm was passed for an RSA key. It now correctly defaults to"SHA256"to match Node.js behavior.
- Fixed a bug where the N-API function napi_strict_equalsincorrectly usedObject.issemantics instead of the===operator. This improves compatibility with native modules that rely on strict equality checks, such asNaN !== NaN.
- Fixed a crash in the N-API function napi_call_functionthat could occur when therecv(thethisvalue) argument was a null pointer.
- Fixed napi_create_array_with_lengthto correctly handle negative or oversized lengths, matching Node.js's behavior.
- Fixed: util.promisify(http2.connect)now resolves correctly, improving Node.js compatibility for thehttp2module.
- Fixed: In child_process, thestdin,stdout,stderr, andstdioproperties are now enumerable to match Node.js behavior. This fixes compatibility with libraries liketinyspawnandyoutube-dl-execthat useObject.assignon the child process object.
- Fixed: Request body streaming when using node-fetchin Bun now works correctly, preventing large request bodies from being fully buffered in memory.
- Fixed: A bug where Buffer.from(string, 'utf-16le')produced incorrect output in rare cases. This improves Node.js compatibility.
- Fixed: an incorrect assertion failure in N-API when napi_reference_unrefis called during garbage collection. This improves compatibility with Node.js and fixes crashes in packages likerolldown-vite.
- Fixed: process.versions.llhttpwas missing, which improves Node.js compatibility.
- Fixed: module._compileis now correctly assigned torequire('module').prototype._compile.
- Fixed: A noisy, un-actionable warning for async_hooksis no longer printed to the console by default, especially when using React or Next.js.
- Fixed: crypto.randomIntwas not calling the callback. Now it does.
- Fixed: new Buffer.isAscii(string)now correctly checks for ASCII; it was previously using theisUtf8implementation by mistake. This did not impactBuffer.isAscii(string), onlynew Buffer.isAscii(string), which you probably shouldn't use anyway.
- Improved: error message in Buffer.concat()when concatenating buffers larger than 4GB. ARangeErroris now thrown with a descriptive message.
Bundler & minifier improvements:
- Fixed: In Bun.build, theonResolveandonLoadplugin hooks now correctly run for entrypoint files, matching esbuild's behavior. (Thanks to @dylan-conway)
- Fixed: Runtime plugins using onResolvenow correctly resolve dynamicimport()calls, which previously could fail with anENOENTerror. (Thanks to @dylan-conway)
- Fixed: Bun.buildnow throws anAggregateErrorby default on build failures. To revert to the old behavior, set{ throw: false }and inspect thesuccessproperty on the returnedBuildOutput. (Thanks to @dylan-conway)
- Fixed: A bug where standalone binaries created with bun --compilewould incorrectly include the executable's name as an extra argument inprocess.argv. This could cause argument parsing libraries likenode:util.parseArgsto fail.
- Fixed: An assertion failure in bun buildthat could occur when using the--compileand--bytecodeflags together.
- Fixed: Bundler plugins can now intercept entry points with onResolve, fixing a bug that prevented the creation of virtual entry points.
- Fixed: A bug where build.module()in a Bun plugin would fail to parse TypeScript syntax when usingloader: 'ts'.
- Fixed: Bun.build()was missing thesplittingproperty in its TypeScript types.
- Fixed: On Windows, single-file executables created with bun build --compileno longer have an incorrect "Original Filename" metadata field set to "bun.exe".
- Fixed: A regression on Windows where bun build --compilewould fail when using embedded resources or a relative path for--outfile.
- Fixed: a memory leak in Bun.pluginwhereonLoadfilters using regular expressions would not be garbage collected, causing memory usage to grow over multiple builds.
- Fixed: A bug causing non-deterministic module resolution for packages that ship both CommonJS and ES Module versions. This could lead to inconsistent builds and "dual package hazard" errors.
- Fixed: an error when parsing linear-gradient()withturnangle units.
- Fixed: A bug where certain modules using top-level await were missing a asynckeyword, causing a runtime error.
- Fixed: A bug where banneroption withformat: "cjs"andtarget: "bun"could produce a syntax error ifbannercontained a shebang.
JavaScript runtime improvements:
- Bun.YAML.parsenow accepts- Buffer,- ArrayBuffer,- TypedArrays,- DataView, and- Blobas input.
- Fixed: Bun.Cookie.isExpired()now correctly returnstruefor cookies with anExpiresdate set to the Unix epoch (Thu, 01 Jan 1970 00:00:00 GMT).
- Fixed: an exception in crypto.subtle.importKeywhen importing RSA private keys with Chinese Remainder Theorem parameters. This resolves a compatibility issue with thejoseJWT library.
- Fixed: a bug that caused fetch()requests with large bodies to fail with anECONNRESETerror when using an HTTP proxy for an HTTPS connection.
- Fixed: Errors thrown inside HTMLRewriterhandlers are now correctly propagated as catchable JavaScript errors, instead of causing a[native code: Exception]message or a crash.
- Fixed: A reliabiltiy issue in HTMLRewriter
- Fixed: an assertion failure when using the BUN_INSPECT_CONNECT_TOenvironment variable in a project that loads environment variables from a.envfile.
- Fixed: a crash when using Bun.secretson Linux.
- Fixed: fetch()throwing aDecompression error: ShortReadwhen receiving an empty response withContent-Encoding: gzipandTransfer-Encoding: chunked. This also occurred with brotli and zstd in some cases.
- Fixed: A bug where structuredClone()would throw a "TypeError: Unable to deserialize data" when cloning nested objects or arrays containing aBloborFile. Additionally, thenameproperty ofFileobjects is now correctly preserved after cloning.
- Fixed: A bug causing error stack traces to be truncated, which made debugging more difficult.
- Fixed: A bug causing fetch()to hang when the server responds with a101 Switching Protocolsstatus, which is used to upgrade a connection to a WebSocket. fetch() in Bun continues to not support WebSocket connections, it now errors instead of hanging.
- Fixed: new WebSocket()now emits anerrorevent before thecloseevent when a connection handshake fails. Previously, only acloseevent was emitted, which was inconsistent with browser behavior.
- process.versionsnow displays semantic versions for- zliband- libdeflateinstead of commit hashes.
- Fixed: A regression causing bun --watchto crash when a file is deleted.
- Fixed: A bug where bun --watchwould not correctly handle changes in swap files.
Bun.SQL bugfixes:
- Fixed: A regression from Bun v1.2.21 impacting DATABASE_URLoptions precedence inBun.SQLconnection string parsing.
- Fixed: A potential crash in Bun.SQLwhen closing a MySQL or PostgreSQL database connection.
Shell improvements:
- Fixed: A shell crash when using environment variable assignments in a pipeline, such as VAR=val | command.
- Fixed: A regression from Bun v1.2.21 where certain commands would trigger an assertion failure on Windows.
bun install improvements:
- Fixed an internal data structure in bun patchto use one-based line indexing by default.
- Fixed: a panic when installing a global package with --trustif it contained dependencies that were already trusted.
- Fixed: A crash that could occur during bun installwhen applying a malformed patch file with out-of-bounds line numbers.
- Fixed: A bug where bun auditcould hang indefinitely due to a cycle in the dependency graph has been resolved.
- Fixed: A bug when extracting tarballs with unusual package names could produce an invalid temporary filename, particularly on Windows.
TypeScript types:
- Fixed: Added missing types for AbortSignal.abortedandRegExp.escapetobun-types.
- Fixed: Added missing TypeScript types for Bun.YAML.parse.