---
title: Bun v1.1.10
description: "Fixes 20 bugs. 2x faster uncached bun install on Windows. fetch() uses up to 2.8x less memory. Several bugfixes to bun install, sourcemaps, Windows reliability improvements and Node.js compatibility improvements"
date: "2024-05-24T22:17:59.149Z"
author: jarred
draft: false
---

Bun v1.1.10 is here! This release fixes 20 bugs. 2x faster uncached bun install on Windows. `fetch()` uses up to 2.8x less memory. Several bugfixes to bun install, sourcemaps, Windows reliability improvements and Node.js compatibility improvements

{% callout %}
We're hiring [systems engineers](/careers) in San Francisco to build the future of JavaScript!
{% /callout %}

#### Previous releases

- [`v1.1.9`](/blog/bun-v1.1.9) fixes 67 bugs (addressing 150 👍). Fixes to: workspaces in `bun install`, sourcemaps in bun build, IPv6 & VPN connectivity, Loading UNC paths, junctions, symlinks, and pnpm on Windows, `fetch()` gets faster, added `dns.prefetch()` API, `atob()` gets 8x faster, `toString('base64url')` gets 5x faster, expect().toBeReturned() matcher, Node.js compatibility improvements, Bun Shell fixes, and lots more bugfixes.
- [`v1.1.8`](/blog/bun-v1.1.8) fixes 54 bugs (addressing 184 👍). Support for `process.on("uncaughtException")` and `process.on("unhandledRejection")`, `JSON.parse` gets faster, Brotli support in `node:zlib`, `[Symbol.dispose]` in Bun APIs, fixes lots of crashes on Windows, and many other bugfixes.
- [`v1.1.0`](/blog/bun-v1.1) Bundows. Windows support is here!

#### To install Bun

{% codetabs %}

```sh#curl
$ curl -fsSL https://bun.sh/install | bash
```

```sh#npm
$ npm install -g bun
```

```sh#powershell
$ powershell -c "irm bun.sh/install.ps1|iex"
```

```sh#scoop
$ scoop install bun
```

```sh#brew
$ brew tap oven-sh/bun
$ brew install bun
```

```sh#docker
$ docker pull oven/bun
$ docker run --rm --init --ulimit memlock=-1:-1 oven/bun
```

{% /codetabs %}

#### To upgrade Bun

```sh
$ bun upgrade
```

## `fetch` uses less memory

`fetch()` gets smarter about when the response body is no longer in use, and releases the memory sooner.

#### 512 KB responses (unconsumed)

In the following code, Bun v1.1.10 uses 2.8x less memory than Bun v1.1.7, and 3.6x less memory than Node v22.

```ts
const server = process.argv.at(-1);
const fmt = new Intl.NumberFormat();
let total = 0;
const batch = 50;
const delay = 32;
while (total < 20_000) {
  for (let i = 0; i < batch; i++) {
    fetch(server);
  }
  await new Promise((r) => setTimeout(r, delay));
  total += batch;
}

console.log(
  "RSS",
  (process.memoryUsage.rss() / 1024 / 1024) | 0,
  "MB after",
  fmt.format((total += batch)) + " fetch() requests",
);
```

After 20,050 requests on a macOS arm64 machine:

| Runtime     | Memory usage |
| ----------- | ------------ |
| Bun v1.1.10 | 166 MB       |
| Bun v1.1.7  | 467 MB       |
| Node v22    | 601 MB       |

#### 512 KB responses (consuming arrayBuffer())

Consuming the body also releases memory sooner. In the following code, Bun v1.1.10 uses 1.6x less memory than Bun v1.1.7, and 10x less memory than Node v22.

```ts
const server = process.argv.at(-1);
const fmt = new Intl.NumberFormat();
let total = 0;
const batch = 50;
const delay = 32;
while (total < 20_000) {
  for (let i = 0; i < batch; i++) {
    fetch(server).then((r) => r.arrayBuffer());
  }
  await new Promise((r) => setTimeout(r, delay));
  total += batch;
}

console.log(
  "RSS",
  (process.memoryUsage.rss() / 1024 / 1024) | 0,
  "MB after",
  fmt.format((total += batch)) + " fetch() requests",
);
```

After 20,050 requests on a macOS arm64 machine:

| Runtime     | Memory usage |
| ----------- | ------------ |
| Bun v1.1.10 | 285 MB       |
| Bun v1.1.7  | 469 MB       |
| Node v22    | 1911 MB      |

Huge thanks to [@cirospaciari](https://github.com/cirospaciari) for this improvement!

### How we made fetch use less memory

JavaScriptCore's garbage collector lets native code hold weak and strong references to JavaScript objects. `fetch()` is especially complicated because it involves:

- `Promise`, which must be kept alive until the Response is fulfilled.
- `Response`, which must be kept alive at least until the headers and status code become available.
- `ReadableStream`, which, if read, must be kept alive while the body is being downloaded.
- Buffered data - `Response` can buffer data into a `Uint8Array`, `ArrayBuffer`, `text`, `json`, `formData`, or `blob`, which must be kept alive while the body is being read.

First, this sets a hard constraint on `Promise` - the `Promise` object must be kept alive at least until we receive the HTTP status code and headers. That means it's a `JSC::Strong`.
The easy thing to do would be to stop here and just keep the Response and it's body alive until the Response is fulfilled and the HTTP response is completely read.
This is the approach we took in Bun v1.1.9 and earlier (before this release).

Once we have the headers and status code, we can release the `Promise`'s strong reference. From there, we need to track the lifetime of the `Response` object. We only need to keep the `Response` object alive as long as it is observable to JavaScript, if the response body is being read. JavaScriptCore's `JSC::Weak` let's us attach a finalizer to a `JSC::JSCell` (a JavaScript object). This finalizer function is called after the `JSC::JSCell` is no longer reachable from JavaScript, which is how we know when the `Response` object is no longer observable to JavaScript.

However, we need to hold a `JSC::Weak` value to the `Response` object, but we cannot access the `Response` object itself once the finalizer is called - the JavaScript object is already freed by then. So the combination of `JSC::Strong` and `JSC::Weak` is not enough to get us all the way here. We need to continue to be able to access the `Response` object after it's no longer accessible to JavaScript, so that we can handle the `ReadableStream` and the buffered data.

For this, we turned to a common manual memory management approach: reference counting. When there's still pending buffered or streaming data from `fetch`, we increment the reference count on the `Response` object in Zig. When the garbage collector calls `Response`'s finalizer, it decrements its reference count. If that reference count is zero, then it frees the Response body and the Response object.

When the garbage collector calls `fetch`'s finalizer notifying it that the `Response` object has been collected, we now know if the body will no longer be accessible and can tell the HTTP client to ignore the response body.

## 2x faster uncached `bun install` on Windows

On Windows, `bun install` gets a 2x speedup when resolving package versions.

```ts
PS C:\bun> hyperfine "bun install --ignore-scripts" "bun-1.1.8 install --ignore-scripts" --prepare="del /s /q bun.lockb && del /s /q C:\Users\window\.bun\install\cache" --warmup=1
Benchmark 1: bun install --ignore-scripts
  Time (mean ± σ):      1.343 s ±  0.398 s    [User: 0.321 s, System: 0.178 s]
  Range (min … max):    0.830 s …  1.861 s    10 runs

Benchmark 2: bun-1.1.8 install --ignore-scripts
  Time (mean ± σ):      3.997 s ±  0.204 s    [User: 0.264 s, System: 0.192 s]
  Range (min … max):    3.753 s …  4.409 s    10 runs

Summary
  bun install --ignore-scripts ran
    2.98 ± 0.89 times faster than bun-1.1.8 install --ignore-scripts
```

We will write a blog post on the technical details of this improvement soon.

### Fixed: Regression in v1.1.9 with hanging `bun install` on Windows

You know how back in the year 2009, non-blocking I/O was a new thing? Well, bun users on Windows experienced a small version of what the time before non-blocking I/O was like.

`bun install` would sometimes hang indefinitely when resolving package versions while the system was under load or the network was flaky. This problem existed earlier on Windows, but was exacerbated in v1.1.9 due to implementing a subset of Happy Eyeballs. The issue was that we were not making the TCP client connection non-blocking until after the socket was already connected. This also caused blocking I/O when connecting via `fetch`, `Bun.connect()`, `new WebSocket()`. It did not impact `Bun.serve()` or `Bun.listen()`.

Fixing this also led to a 20% performance improvement to `bun install` on Windows, compared to v1.1.8 (before the regression).

### Fixed: `ENOENT` parsing package.json error

A regression introduced in Bun v1.1.9 where `bun add <package-name>` inside a subfolder within a workspace package would fail in certain cases has been fixed, thanks to [@dylan-conway](https://github.com/dylan-conway).

### Fixed: package-lock.json migration with workspaces fix

Bun supports automatically migrating from `package-lock.json` -> `bun.lockb`, but an error could occur when that `package-lock.json` contained workspace packages. This has been fixed, thanks to [@dylan-conway](https://github.com/dylan-conway).

### Fixed: Crash after modifying "overrides" or "resolutions"

A crash that could occur on the next `bun install` after modifying the `overrides` or `resolutions` fields in `package.json` has been fixed, thanks to [@gvilums](https://github.com/gvilums).

## New: `setDefaultTimeout` in bun:test

`bun:test` now supports the `setDefaultTimeout` function to modify the default timeout for tests in the current scope or module.

```ts
import { test, setDefaultTimeout } from "bun:test";

// Timeout after 10 milliseconds:
setDefaultTimeout(10);

test("timeout", async () => {
  await Bun.sleep(9999999);
});
```

Previously, you'd have to set the timeout for each test individually:

```ts
import { test } from "bun:test";

test("timeout", async () => {
  await Bun.sleep(9999999);
}, 10);
```

You can still set the timeout for each test individually.

For Jest compatibility, we've also implemented `jest.setTimeout` in `bun:test`.

```ts
import { test, jest } from "bun:test";

jest.setTimeout(10);

test("timeout", async () => {
  await Bun.sleep(9999999);
});
```

We chose to name `bun:test`'s version `"setDefaultTimeout"` instead of `"setTimeout"` to avoid confusion with the global `setTimeout` timer function.

Thanks to [@dylan-conway](https://github.com/dylan-conway) for this feature!

## Fixed: Potential hang in spawned processes on macOS

A bug where spawned processes could hang indefinitely while reading from stdout, stderr, or stdin has been fixed.

#### Why did this happen?

On macOS, calling `send(2)` with the `MSG_NOWAIT` flag with a blocking socket still blocks. On Linux, `MSG_NOWAIT` causes it to not block. So if you managed to send a very large amount of data to a socket, the parent process would potentially block.

Unlike pipes, marking one end of a socket as non-blocking is not observable to the other end.

To verify this, we can compile the following C program:

```c
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main() {
  // Sleep for 10 seconds so that the other process has time to mark stdout as
  // non-blocking
  sleep(10);

  // Check if stdout is blocking or non-blocking
  int stdout_flags = fcntl(fileno(stdout), F_GETFL);
  if (stdout_flags & O_NONBLOCK) {
    printf("stdout is non-blocking\n");
  } else {
    printf("stdout is blocking\n");
  }

  // Check if stderr is blocking or non-blocking
  int stderr_flags = fcntl(fileno(stderr), F_GETFL);
  if (stderr_flags & O_NONBLOCK) {
    printf("stderr is non-blocking\n");
  } else {
    printf("stderr is blocking\n");
  }

  // Check if stdin is blocking or non-blocking
  int stdin_flags = fcntl(fileno(stdin), F_GETFL);
  if (stdin_flags & O_NONBLOCK) {
    printf("stdin is non-blocking\n");
  } else {
    printf("stdin is blocking\n");
  }

  return 0;
}
```

And then spawn it in Bun:

```ts
import { spawn, $ } from "bun";

await $`cc -o a.out a.c`;

const { stdout } = spawn({
  cmd: ["a.out"],
  stdout: "pipe",
  stderr: "pipe",
  stdin: "pipe",
});

console.log(await new Response(stdout).text());
```

The output will be:

```
stdout is blocking
stderr is blocking
stdin is blocking
```

This output is correct because blocking stdout, stderr, and stdin are necessary for many UNIX programs to work correctly. For example, `cat` will not work correctly if stdout is non-blocking and returns `EAGAIN`. We want Bun to not have blocking stdout, stderr, and stdin, but it's very important that programs relying on blocking stdout, stderr, and stdin continue to work correctly.

So we can safely mark the socket as non-blocking without affecting the other end.

To prevent future regressions, we've added a regression test that writes & reads large amounts of data to another process in the right conditions to cause this error, and verified that this test previously failed on macOS.

## Fixed: Sourcemaps invalid JSON with `--splitting` in `bun build`

A bug where sourcemaps in certain cases were not generating a correct JSON object when using `--splitting` in `bun build` has been fixed, thanks to [@paperclover](https://github.com/paperclover). This bug was caused by incorrectly joining sourcemap strings from separate files. To prevent future regressions, we've improved our test coverage to verify sourcemaps are valid.

More improvements to sourcemaps in Bun are coming soon.

## Windows fixes

### Fixed: 1s delay to Worker exit

An event loop bug caused a 1s delay to Worker exit on Windows. This has been fixed, thanks to [@gvilums](https://github.com/gvilums).

### Backslashes in import paths in `bun build`

A bug where an unescaped backslash could be inserted in `bun build`'s import specifiers has been fixed, thanks to [@paperclover](https://github.com/paperclover).

### Fixed: `bun --watch` not killing instances of Bun

Previously, when using `bun --watch`, instances of Bun could be left running after exiting.
This issue has been fixed, thanks to [@paperclover](https://github.com/paperclover).

## Node.js compatibility improvements

### Fixed: EventEmitter in streams bug

A bug where event listeners removed within an `emit` call would be skipped has been fixed. This bug impacted `node:stream`, but did not impact `node:events`. Thanks to [@gvilums](https://github.com/gvilums) for fixing this.

This bug impacted Postgres.js when closing connections.

## Crash report uploading

On macOS & Linux, Bun will attempt to automatically upload bun.report links to our crash reporting service when a crash occurs. This will help us fix bugs faster. You can disable this by setting `BUN_CRASH_REPORTER_URL=""`. Please continue to report crashes to us on GitHub, as it really helps us fix bugs. [Learn more bun.report](https://bun.sh/blog/bun-report-is-buns-new-crash-reporter).

On Linux, this is enabled in the canary build of Bun but not on the release build.

## Thanks to 9 contributors!

- [@cirospaciari](https://github.com/cirospaciari)
- [@dylan-conway](https://github.com/dylan-conway)
- [@eigilsagafos](https://github.com/eigilsagafos)
- [@gvilums](https://github.com/gvilums)
- [@Jarred-Sumner](https://github.com/Jarred-Sumner)
- [@jess-render](https://github.com/jess-render)
- [@Marukome0743](https://github.com/Marukome0743)
- [@nektro](https://github.com/nektro)
- [@paperclover](https://github.com/paperclover)
- [@SunsetTechuila](https://github.com/SunsetTechuila)
