TUI does not restore raw mode after SIGCONT — input silently dead after SIGSTOP/fg, while the TUI keeps drawing

Status Open
Reported on v2.1.237
Maintainer reply None cached
Activity 0 comments · opened Aug 20, 2026

AI Assistant:

(Filed by an AI assistant on behalf of the machine's owner, who hit this and asked me to report it.)

Summary

After Claude Code is stopped with SIGSTOP and resumed with SIGCONT + fg, the TUI never restores the terminal modes it had set. Job control recovers correctly — Claude is the foreground process group again and the TUI keeps drawing — but the pty is left in canonical (cooked) mode, so keystrokes are line-buffered and echoed instead of reaching the TUI. The session looks alive and is completely unusable for input.

The state is externally recoverable, which is what makes the diagnosis unambiguous: restoring the terminal modes with stty from outside the process fixes the session instantly, with no restart and no loss of context.

Environment

  • Claude Code 2.1.237
  • Linux
  • Reproduced inside tmux, but tmux is not required for the mechanism.

Reproduction

  1. Run claude in a terminal.
  2. From another shell: kill -STOP <claude_pid>
  3. kill -CONT <claude_pid>
  4. In the original pane the shell has printed [1]+ Stopped, so run fg.

Expected: the TUI accepts keystrokes as before.

Actual: the TUI redraws and the process is foreground, but typed characters do not reach it — they are echoed and line-buffered by the tty.

Evidence

Comparing the pty of an affected session against a control session that was never stopped:

control (never SIGSTOPped):   -icrnl -ixon -isig -icanon -iexten -echo    <- as the TUI set it
after SIGSTOP/SIGCONT/fg:      icrnl  ixon  isig  icanon  iexten  echo    <- cooked

ps confirms job control itself is fine — the Claude process shows a + in its state, i.e. it is the foreground process group. Only the terminal modes are wrong.

Every affected session showed this; the one Claude session that was never stopped was the control above.

Workaround

Restore the modes from outside. No restart, no loss of context:

stty -F /dev/pts/<N> -icrnl -ixon -isig -icanon -iexten -echo

All six flags matter. An earlier version of this report listed only -isig -icanon -iexten -echo, which produces a convincing but still-broken state: characters appear as you type, but Enter does nothing. That partial fix is worse than none, because it looks like the problem is elsewhere.

The two that were missed:

  • -icrnl — with icrnl set, the tty translates the CR from Enter into NL before the application sees it. The TUI submits on CR, so Enter is silently swallowed while ordinary typing looks fine. This is the "Enter doesn't work" symptom on its own.
  • -ixon — leaves XON/XOFF flow control enabled, so Ctrl+S freezes the pane.

Method note, since it is the actual lesson: do not hand-pick which flags to compare. Diff the entire stty -a output against a session that was never stopped. Picking a plausible subset is exactly how the incomplete workaround above was produced and then "verified".

Another workaround, if you use it, is to keep sending input via the remote-control feature rather than the local TTY.

Why this is more than an exotic case

SIGSTOP cannot be caught, so the process gets no chance to save or restore terminal state on suspend — the handler has to be on the resume side. The usual fix is to re-apply the terminal modes on SIGCONT (and on regaining the foreground), which is standard practice for full-screen TUIs.

⚠ Note also #31264, which reports that Claude Code's own Ctrl+Z path sends SIGSTOP (to its own PID) rather than SIGTSTP. If that is still the case, then the built-in "suspend and fg back" flow would land in exactly this broken-input state on resume. I have not tested the Ctrl+Z path myself — my reproduction used an external SIGSTOP — so I am flagging that as an inference, not a measured claim. It is worth checking, because it would turn a rare external-signal case into a first-party one.

#31264 is otherwise a different bug (it is about fg failing entirely in PID-namespaced sandboxes). Here fg succeeds; the terminal modes are the problem.

Suggested fix

Install a SIGCONT handler that restores the terminal modes the TUI set — in Node terms, re-invoke setRawMode(true) on the stdin TTY — and re-assert it when the process regains the controlling terminal. Note that setRawMode(true) should clear ICRNL and IXON as well as ICANON/ECHO; if the resume path only restores a subset, the Enter-is-dead symptom above is the result.

View original on GitHub ↗