fix(init): restore /dev/tty workaround on macOS only#836
Merged
Conversation
PR #835 deleted forwardFreshTtyToStdin on the theory that Bun 1.3.11 fixes the inherited-TTY keystroke-delivery bug that the workaround was written for. That's true on **Linux** (verified via PTY harness mimicking install.sh's `exec bin </dev/tty` flow). It's **not** true on **macOS**: `curl -fsSL https://cli.sentry.dev/install | SENTRY_INIT=1 bash` still hangs at the first clack prompt ("EXPERIMENTAL: ... Continue? Yes / No") — user can't even type. Restore the workaround, gated on `process.platform === "darwin"`: - On Linux: no workaround installed, loop drains naturally, wizard exits cleanly with no safety net. Keeps the \-838 LOC win from #835. - On macOS: workaround installs, keystrokes deliver, BUT the second `tty.ReadStream` leaks a libuv handle (upstream oven-sh/bun#29126) and the event loop can't drain. Restore the `setTimeout(process.exit, 100).unref()` safety net from #833, also gated to darwin, to force-exit after the wizard returns. The safety net is additionally gated on `NODE_ENV !== "test"` so `bun test` (which sets this automatically) doesn't accumulate unref'd timers that could fire between test files and terminate the runner mid-suite. All prior test coverage restored (`test/lib/init/stdin-reopen.test.ts` is unchanged from its state before #835 — 15 tests covering install/teardown lifecycle via `/dev/ptmx` fixture). Follow-ups: - Ship via nightly; validate `sentry init` exits cleanly on macOS (Terminal.app, iTerm2), Linux glibc, Linux musl, and WSL. - File a Bun upstream bug for the macOS compiled-binary keystroke delivery issue. This is distinct from oven-sh/bun#29126 which is about `process.stdin` readable listener removal.
Contributor
|
Contributor
Codecov Results 📊✅ 138 passed | Total: 138 | Pass Rate: 100% | Execution Time: 0ms 📊 Comparison with Base Branch
✨ No test changes detected All tests are passing successfully. ✅ Patch coverage is 100.00%. Project has 1934 uncovered lines. Coverage diff@@ Coverage Diff @@
## main #PR +/-##
==========================================
+ Coverage 95.30% 95.32% +0.02%
==========================================
Files 283 284 +1
Lines 41243 41350 +107
Branches 0 0 —
==========================================
+ Hits 39306 39416 +110
- Misses 1937 1934 -3
- Partials 0 0 —Generated by Codecov Action |
Contributor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit dff61e0. Configure here.
…erminate too Cursor Bugbot caught that the macOS force-exit `setTimeout().unref()` was placed AFTER `await runWizard()`. On error paths (WizardError from network timeout, API error, workflow error result, missing suspend payload) the throw bypassed the timer entirely, so the process would hang on the error display after the workaround's leaked libuv handle kept the loop alive. Wrap `runWizard` in try/finally so the timer is scheduled on every exit path: success, WizardError, user cancel, unknown throws. The throw still propagates to the CLI wrapper (Stricli sets exitCode from the error) before the 100ms timer fires, so exit codes are preserved.
BYK
added a commit
to BYK/bun
that referenced
this pull request
Apr 23, 2026
…inal On macOS, `open_as_nonblocking_tty` reopens an inherited TTY fd by calling `ttyname_r(fd)` + `open(pathbuf, O_NONBLOCK | O_NOCTTY | O_CLOEXEC)`. The resulting slave-path fd has kqueue `EVFILT_READ` wiring that does NOT reliably deliver read events when registered from a non-session-leader process — the kernel accepts the `EV_ADD` but never fires events for the inherited fd. Symptom: compiled Bun binaries on macOS launched via `exec bin </dev/tty` (the standard `curl | bash` installer pattern) have `process.stdin.isTTY === true` and accept `setRawMode(true)`, but keystrokes never reach the `data` listener. Clack/Ink-style TUIs hang on their first prompt. Linux is unaffected. Fix: when the inherited fd is our controlling terminal (`tcgetsid(fd) == getsid(0)`), open `/dev/tty` directly instead. This routes through the kernel's cttydev lookup at open time and produces an fd with working kqueue wiring — the same approach that Bun's existing regression test `test/regression/issue/tui-app-tty-pattern.test.ts` validates for user-constructed `new tty.ReadStream` workarounds. Guarded on `tcgetsid == getsid` so the fast-path only applies when `fd` actually IS our controlling terminal. Falls back to the original `ttyname_r` path if `/dev/tty` open fails (e.g. sandbox, daemonized process). Linux and FreeBSD unchanged. See also: oven-sh#24158 (confirmed bug, WriteStream side) oven-sh#26792 (same root cause on Linux-RHEL8 equivalent) oven-sh#26274 (prior broader macOS kqueue skip, closed due to test failures — this patch is narrower) https://nathancraddock.com/blog/macos-dev-tty-polling/ Adds a regression test (`test/regression/issue/stdin-fd0-macos-ctty.test.ts`) that reproduces the exact bug via Python's `pty.fork()`, mirroring install.sh's `exec bin </dev/tty` flow. The test passes on Linux (bug doesn't reproduce there) and must pass on macOS after this patch. Real-world motivation: the getsentry/cli team shipped a userland workaround (a fresh `/dev/tty` ReadStream forwarding data to `process.stdin`) in getsentry/cli#836 to unblock users hitting this bug. With this patch upstream they can eventually delete that workaround.
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
PR #835 deleted
forwardFreshTtyToStdinon the theory that Bun 1.3.11 fixes the inherited-TTY keystroke-delivery bug that the workaround was written for. That's true on Linux (verified via PTY harness). It's NOT true on macOS:curl -fsSL https://cli.sentry.dev/install | SENTRY_INIT=1 bashhangs at the first clack prompt — user can't type.Restore the workaround, gated on
process.platform === "darwin".User-reported reproduction
After install, wizard shows:
Keystrokes (Y/N/Enter) don't reach clack. Prompt hangs indefinitely. Exactly what
forwardFreshTtyToStdinwas fixing originally.Fix
src/lib/init/wizard-runner.tsGate the
using _tty = forwardFreshTtyToStdin()install onprocess.platform === "darwin":src/commands/init.tsRestore the
setTimeout(process.exit, 100).unref()safety net from #833, also gated to darwin:The safety net is needed on macOS because the workaround opens a second
tty.ReadStreamwhich leaks a libuv handle (upstream oven-sh/bun#29126). On Linux that leak doesn't occur (no workaround installed). TheNODE_ENV=testgate ensuresbun testdoesn't accumulate unref'd timers mid-suite.Restored (unchanged from pre-#835 state)
src/lib/init/stdin-reopen.ts(320 lines) — includes all hardening from fix(init): harden /dev/tty teardown and adopt Symbol.dispose #824 (using/Symbol.dispose, termios restore), fix(init): release stdin handle to unblock event loop on wizard exit #831 (original.pause.callin teardown), and fix(init): add force-exit safety net for Bun fresh+readline hang #833-era CI fixes (Object.definePropertyforisTTY).test/lib/init/stdin-reopen.test.ts(452 lines) — 15 tests covering install/teardown lifecycle via/dev/ptmxfixture.Test plan
bun test test/lib/init/ test/commands/init.test.ts— 193 passbun test --timeout 15000 test/lib test/commands test/types— 5777 pass, 0 failbun run typecheck— cleanbun run lint— clean (only pre-existing markdown.ts warning)curl -fsSL https://cli.sentry.dev/install | SENTRY_VERSION=nightly SENTRY_INIT=1 bash. Expect: first clack prompt accepts keystrokes, wizard completes, shell returns promptly after "Setup complete".Risk
Low. Platform-gated to darwin, so any bugs in the workaround only affect macOS users. Non-Darwin path unchanged from #835 (proven clean on Linux). If macOS has further issues, the revert is a single commit away.
Follow-ups
process.stdin's readable-listener polling). A minimal repro running the stockreadlinemodule on a redirected/dev/ttyfd should be straightforward.