[BUG] Terminal left in raw mode after CTRL+D exit
Terminal left in raw mode after CTRL+D exit
Bug Description
Exiting claude-code via CTRL+D (EOF) leaves the terminal in raw mode. After exit, control sequences are printed literally (e.g. ^C, ^D) instead of being interpreted by the shell. The terminal is non-functional until manually reset.
Steps to Reproduce
- Note terminal state:
stty -g > /tmp/stty-before - Launch
claude - Press CTRL+D to exit
- Observe broken terminal — control characters print literally
- Capture broken state:
stty -g > /tmp/stty-after(typed blind) diff /tmp/stty-before /tmp/stty-afterconfirms raw mode settings persist
Expected Behavior
Terminal is restored to its prior state on exit, regardless of exit method (CTRL+D, /exit, CTRL+C).
Actual Behavior
Terminal is left in a corrupted state. stty sane does not fix it — only reset (which sends RIS / \x1Bc) recovers the terminal. This indicates the problem is not limited to stty line discipline settings; terminal emulator state is also left dirty. Likely culprits:
- Bracketed paste mode left enabled (
\x1B[?2004h) - Focus reporting left enabled (
\x1B[?1004h) - Possibly other DEC private modes
Root Cause
The CTRL+D (EOF) code path appears to exit the process before Ink's React unmount lifecycle completes. Ink's cleanup — which calls process.stdin.setRawMode(false) and writes \x1B[?2004l to disable bracketed paste — never executes.
Both the kernel terminal driver state (raw mode) and the terminal emulator state (DEC private modes) need to be restored. A process.on('exit', ...) handler should unconditionally:
- Call
process.stdin.setRawMode(false) - Write reset sequences to stdout (
\x1B[?2004l,\x1B[?1004l, etc.)
Workaround
# Wrapper function — must restore both stty state AND terminal emulator state
claude() {
local saved
saved=$(stty -g)
command claude "$@"
stty "$saved"
printf '\x1b[?2004l\x1b[?1004l'
}
Environment
- Claude Code version: 2.1.84
- OS: Gentoo Linux (amd64)
- Kernel: 6.x (custom, AMD X870)
- Terminal: foot
- Shell: bash + starship prompt
- Installation method: Binary package (Gentoo ebuild)
Related Issues
- #12483 — Terminal state corruption after CTRL+Z suspension (same root cause, different signal)
- #3355 — Raw mode persists after
/memorycommand with vim - #31557 — Broken terminal state on exit (Windows)
All were closed by the inactivity bot without resolution.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗