[BUG] Claude Code suspends with SIGTTIN/SIGTTOU on launch (macOS 26)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
On macOS 26.3.1 (Darwin 25.3.0) Apple Silicon, launching claude from any newly
opened terminal session causes the process to be immediately suspended by the
kernel with SIGTTOU (or SIGTTIN) right after the banner renders. The shell
reports e.g. fish: Job 1, 'claude' has stopped or zsh: suspended (tty input).
claude
The bug is reproducible across:
- fish 4.5.0 (with and without --no-config)
- interactive zsh in a freshly opened Terminal.app window
- inside Ghostty as well as Terminal.app
Striking data point: two claude processes that I started ~21 hours and ~18
hours ago (before the bug appeared) are still running fine on long-lived shell
sessions on my machine. Only newly-launched shell sessions are affected. This
implies the responsible change happened in the last 24h on this machine.
What Should Happen?
$ claude
▐▛███▜▌ Claude Code v2.1.129
▝▜█████▛▘ Opus 4.7 with high effort · API Usage Billing
▘▘ ▝▝ /Users/<me>
❯ fish: Job 1, 'claude' has stopped
# In zsh:
$ claude
... (banner) ...
zsh: suspended (tty input) claude
zsh: error on TTY read: Input/output error
[Process completed]
# Diagnostic snapshot of the stopped process:
$ ps -o pid,stat,ppid,pgid,tpgid,command -p <claude-pid>
PID STAT PPID PGID TPGID COMMAND
9200 T 97412 9200 11341 claude
# Note: TPGID (terminal foreground pgrp) = shell pid, while claude's PGID = its own pid.
# claude is therefore NOT the TTY's foreground process group when it runs tcsetattr.
# sample(1) stack of the stopped process:
1724 Thread … DispatchQueue_1: com.apple.main-thread
+ ... (claude internals) ...
+ 1724 tcsetattr (in libsystem_c.dylib) + 172
+ 1724 ioctl (in libsystem_kernel.dylib) + 36
+ 1724 __ioctl (in libsystem_kernel.dylib) + 8
# tcsetattr from a non-foreground process group → SIGTTOU → stopped.
Error Messages/Logs
1. macOS 26.3.1 / Apple Silicon / claude code 2.1.129
2. Open a new terminal window (Ghostty or Terminal.app — both reproduce)
3. In the resulting interactive shell (fish or zsh), run: claude
4. Banner renders, then the shell reports the job suspended.
Notes:
- Reproduces under `fish --no-config` (rules out user fish config / hooks)
- Reproduces under interactive zsh in a freshly launched Terminal.app window
- Pre-existing shell sessions started >18h ago do NOT reproduce — `claude`
launches fine in them.
Steps to Reproduce
- macOS 26.3.1 / Apple Silicon / claude code 2.1.129
- Open a new terminal window (Ghostty or Terminal.app — both reproduce)
- In the resulting interactive shell (fish or zsh), run: claude
- Banner renders, then the shell reports the job suspended.
<img width="2404" height="410" alt="Image" src="https://github.com/user-attachments/assets/24477fc3-7315-4155-80c2-c08940685f2e" />
Notes:
- Reproduces under
fish --no-config(rules out user fish config / hooks) - Reproduces under interactive zsh in a freshly launched Terminal.app window
- Pre-existing shell sessions started >18h ago do NOT reproduce —
claude
launches fine in them.
Claude Model
Opus
Is this a regression?
I don't know — I don't have a confirmed last-working version, but two
pre-existing claude processes started ~24h ago are still running, suggesting
something on the system or in the binary changed within the last day.
Last Working Version
_No response_
Claude Code Version
2.1.129 (Claude Code)
Platform
Anthropic API
Operating System
macOS (specifically macOS 26.3.1 / Darwin 25.3.0, arm64)
Terminal/Shell
Other — Ghostty (also reproduces in Apple Terminal.app); shells tested: fish 4.5.0 and zsh.
Additional Information
Suspected root cause:
The claude binary appears to call tcsetattr() during startup without first
ensuring it is the controlling TTY's foreground process group, and without
ignoring SIGTTOU. POSIX requires that tcsetattr() from a process not in the
foreground process group raises SIGTTOU on the calling process (regardless of
the orphaned-process-group exception). When the host shell does not call
tcsetpgrp() to hand TTY ownership to claude before exec, claude is stopped
immediately.
This explains the observed cross-shell reproducibility: neither fish 4.5.0 nor
zsh appears to issue tcsetpgrp() for foreground external commands in the
default interactive job-control mode (relying on the child to manage its own
process group).
Suggested fixes (any one of these would prevent the suspension):
- At startup, before any tcsetattr():
signal(SIGTTOU, SIG_IGN);
- Or explicitly take TTY ownership:
setpgid(0, 0);
if (isatty(STDIN_FILENO)) tcsetpgrp(STDIN_FILENO, getpgrp());
- Or guard the tcsetattr() call:
if (tcgetpgrp(STDIN_FILENO) != getpgrp()) { / skip raw-mode setup / }
Workarounds tried on user side (all unsatisfactory):
status job-control fullin fish — does not consistently fixscript -q /dev/null claude(BSD script PTY wrapper) — claude starts but
stdin forwarding is unreliable; claude exits soon after
- python3
pty.spawn— works for input but PTY size defaults to 80×24 and
SIGWINCH is not forwarded by stdlib pty.spawn
Diagnostic evidence in this report was collected with:
ps -o pid,stat,ppid,pgid,tpgid,command -p <pid>sample <pid> 2 -mayDie(macOS native sampler)lsof -p <pid>- comparison across fish/zsh and across pre-existing vs new shell sessions.
Two pre-existing claude processes that started ~21h and ~18h ago are still
running fine on this machine. Only newly-launched shell sessions reproduce the
bug. Snapshot:
$ ps -o pid,ppid,tty,stat,etime,%cpu,command -p 1207 1152 68247 68155
PID PPID TTY STAT ELAPSED %CPU COMMAND
1152 1151 ttys000 S 21:44:54 0.0 -/opt/homebrew/bin/fish --login
1207 1152 ttys000 S+ 21:44:45 0.1 claude
68155 68154 ttys007 S 18:43:17 0.0 -/opt/homebrew/bin/fish --login
68247 68155 ttys007 S+ 18:43:15 0.0 claude
Note STAT=S+ (foreground process group, sleeping) for the healthy claudes,
versus STAT=T (stopped, SIGTTOU) for any newly-launched claude in this same
machine right now. Whatever made the foreground-group handoff work for these
two pre-existing sessions stopped working after they were started.
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗