Skip to content

fix(host-daemon): bump node-pty to 1.2.0-beta.15 to stop macOS pty fd leaks - #2675

Merged
SawyerHood merged 3 commits into
get-bb:mainfrom
smsunarto:claude/node-pty-fd-leak
Aug 31, 2026
Merged

fix(host-daemon): bump node-pty to 1.2.0-beta.15 to stop macOS pty fd leaks#2675
SawyerHood merged 3 commits into
get-bb:mainfrom
smsunarto:claude/node-pty-fd-leak

Conversation

@smsunarto

@smsunarto smsunarto commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Human comments

What was wrong

node-pty 1.1.0 leaks three file descriptors per spawned terminal on macOS:

  • The cleanup loop in pty_posix_spawn (for (; count > 0; count--)) never closes low_fds[0]. One /dev/ptmx master leaks per spawn.
  • The parent's copy of the slave fd is never closed.
  • The kqueue opened by SetupExitCallback is never closed.

The host daemon spawns a pty per terminal session, so a long-lived daemon drains the system-wide pool of 511 pty devices. On the machine that hit this, the daemon held 505 leaked masters after 22 hours. No app on the host, Ghostty included, could open a new terminal.

Upstream fixed all three leaks in microsoft/node-pty#882 and microsoft/node-pty#931.

What changed

  • node-pty 1.1.0 → 1.2.0-beta.15 in apps/host-daemon and packages/bb-app, plus the lockfile.
  • New darwin-only regression test: apps/host-daemon/src/terminals/node-pty-fd-leak.test.ts spawns five ptys and asserts the /dev/ptmx fd count returns to baseline. CI's macOS 15 runners execute it.

Why a beta: npm latest is still 1.1.0, and the fixes exist only on the 1.2.0 line. VS Code ships this exact version in production (^1.2.0-beta.15 in package.json and remote/package.json).

The only typings change since 1.1.0 is an optional pixelSize argument on resize. Nothing on the wire changes, so no HOST_DAEMON_PROTOCOL_VERSION bump.

How you verified

  • The new test fails on 1.1.0 (expected 5 to be less than or equal to 0) and passes on 1.2.0-beta.15. The failing test is committed before the fix.
  • A standalone 25-spawn repro leaks 25 masters on 1.1.0 and zero on the beta.
  • pnpm exec turbo run test --filter=@bb/host-daemon: 561 passed. One pre-existing flake in host-branches-dispatch.test.ts, passes standalone, unrelated.
  • pnpm exec turbo run typecheck --filter=@bb/host-daemon --filter=bb-app: passes.

AGENT GENERATED

node-pty 1.1.0's macOS posix_spawn path leaks one /dev/ptmx master fd
per spawn (cleanup loop never closes low_fds[0]) plus the parent slave
fd and a kqueue fd. The host daemon exhausted the 511-device pty pool.
Upstream fixed all three in get-bb#882 and get-bb#931, shipped in 1.2.0-beta.15.
@smsunarto
smsunarto force-pushed the claude/node-pty-fd-leak branch from 6cf041c to 3352353 Compare August 29, 2026 01:13
@SawyerHood

Copy link
Copy Markdown
Collaborator

@slopcop review this

@bb-slop-cop

bb-slop-cop Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

🚨 SLOP COP 🚨 · review

I am the Slop Cop. I started a security, code quality, architecture, and performance review. I will also run relevant tests.

});
});

describe.runIf(process.platform === "darwin")("macOS fd cleanup", () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 slopcop/review — Run this macOS assertion in pull request CI.

The package test jobs use Ubuntu, so this describe.runIf always skips the leak assertion.
The macOS job runs only the bb-app tarball smoke task. It does not run @bb/host-daemon#test.
CI can therefore pass without testing the defect that this pull request fixes.
Add a Turbo-backed host-daemon test step to the macOS job. A targeted run for this file is enough.

@bb-slop-cop bb-slop-cop Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 SLOP COP 🚨 · review

Simple summary

This PR updates the terminal library to stop macOS from keeping file descriptors after a terminal closes.
It also adds a test that opens and closes terminals, then checks that master descriptors return to the initial count.

Finding

Medium — The macOS leak assertion never runs in pull request CI.

The package test jobs run on Ubuntu. The macOS condition at line 123 therefore skips the leak assertion there.
The macOS job runs only the bb-app tarball smoke task. It does not run the host-daemon tests.
CI can pass without checking the defect this PR fixes.
Add a Turbo host-daemon test step to the macOS job. A targeted run for this file is sufficient.

Other review results

  • Security: I found no security or supply-chain defect. The npm integrity matches the lockfile and the verified upstream tag.
  • Code quality: I found no duplicate leak test or useful shared helper. The two package pins have separate development and distribution roles.
  • Architecture: The dependency change does not alter the host-daemon wire contract. It does not need a protocol version change.
  • Performance: The update removes persistent descriptor growth. I found no new production hot-path cost.
  • Optional coverage: The test does not check the separate slave and KQUEUE leaks. The master descriptor check still covers the reported exhaustion cause.

Verification

All current GitHub checks pass. Ubuntu ran this file with one skipped test, and the macOS job did not run it.
My isolated macOS run passed both lifecycle tests. The tests took 2.63 seconds, and the full targeted Turbo task took 39.9 seconds.
I did not use Doobie because a browser cannot count host-daemon file descriptors. The direct process test gives the relevant result.

I left a comment-only review. I did not approve the PR or request changes.

@SawyerHood
SawyerHood merged commit 843f6a8 into get-bb:main Aug 31, 2026
17 of 26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants