[BUG] CLI suspended with SIGTTIN after Bash tool child processes exit

Resolved 💬 3 comments Opened Apr 13, 2026 by Screendead Closed May 24, 2026

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?

After a Bash tool command finishes (especially commands >5 seconds like sf project deploy), Claude Code gets suspended instead of returning to the prompt:

[1]  + 52254 suspended (tty input)  claude --resume "session-name"

fg %1 recovers it, but it then exits itself gracefully (showing the "Resume this session with:" message). On --resume, it suspends again after the next Bash tool command. Reproduces across VS Code integrated terminal and macOS Terminal.app — not terminal-specific.

Each suspension also writes a stop_reason: null streaming partial to the session JSONL. These accumulate over repeated suspensions — one affected session had 367/683 (54%) assistant messages incomplete. On --resume, all lines replay to the API including the partials, eventually making the session unresumable.

What Should Happen?

Claude should remain in the foreground and return to the input prompt after Bash tool child processes exit.

Error Messages/Logs

[1]  + 52254 suspended (tty input)  claude --resume de2a2706-6c34-41a4-abdf-23380a041ba3


No stack trace — the process is suspended by SIGTTIN, not crashed.

Steps to Reproduce

Not reliably reproducible with simple commands (e.g. sleep 10 does not trigger it). It occurs intermittently during real usage with these conditions:

  • Long-running sessions (200+ minutes)
  • Complex child processes that may manage their own process groups (e.g. Salesforce CLI sf project deploy, sf agent activate, npx jest — Node.js tools that spawn their own child processes)
  • Default zsh job control (set -m, the default for interactive shells)

When it triggers:

  1. Bash tool command completes
  2. Instead of returning to the prompt, the terminal shows suspended (tty input)
  3. fg %1 — Claude wakes up but immediately exits gracefully
  4. claude --resume "session-name" — session resumes, but suspends again after the next complex Bash tool command

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.92 (Claude Code) — latest Homebrew cask version; npm latest is 2.1.101

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

VS Code integrated terminal

Additional Information

Suspected root cause: SIGTTIN means Claude is no longer in the terminal's foreground process group when it tries to read stdin. set +m (which disables process group separation) prevents the issue entirely. This suggests the Bash tool's child process somehow causes Claude to lose foreground status — possibly by taking over the foreground process group and not restoring it on exit, or through some other process group / terminal control interaction in Node.js's child_process or the Ink TUI framework. I haven't read the source so this is inference from symptoms, not confirmed.

Workaround: set +m && claude — disabling zsh monitor mode prevents process group separation, so there's no foreground group to lose. This prevents the SIGTTIN suspension, but in VS Code's integrated terminal Claude still exits gracefully mid-session (no suspension, no error — just quits). In standalone Terminal.app, set +m appears to fully resolve the issue. The graceful exit in VS Code may be a separate bug in VS Code's PTY layer.

Session corruption detail: Streaming partial messages (stop_reason: null) are written to the session JSONL during the suspension. These are meant to be ephemeral display state but get persisted permanently. Cleaning them (removing partials, fixing stop_reason on remaining messages) restores the session. Attached: clean-session-jsonl.py — a script that backs up and cleans affected session files.

Tested in multiple terminals: Initially suspected VS Code's PTY, but the issue reproduces in standalone macOS Terminal.app with vanilla zsh — confirming it's in Claude Code's own process group management.

Note on 2.1.101 changelog: Two fixes in 2.1.101 may mitigate the session corruption (secondary effect) but not the SIGTTIN suspension itself:

  • "Fixed --resume/--continue losing conversation context on large sessions when the loader anchored on a dead-end branch instead of the live conversation" — the stop_reason: null partials likely create these dead-end branches
  • "Fixed --resume chain recovery bridging into an unrelated subagent conversation when a subagent message landed near a main-chain write gap" — a SIGTTIN suspension mid-write creates exactly this kind of gap

I haven't verified these fixes resolve the secondary corruption since Homebrew lags behind npm. The primary bug (SIGTTIN after Bash child exit) is not addressed in any changelog entry up to 2.1.101.

Related issues (same signal family, different triggers):

  • #11898 — SIGTTIN/SIGTTOU on iTerm2 focus change
  • #11439 — TTIN on Ctrl-G external editor launch
  • #12483 — Terminal state corruption after Ctrl+Z

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗