Hang: spurious /dev/tty reader
Summary
Claude Code becomes permanently unresponsive after certain shell commands. The
root cause is that Claude Code opens /dev/tty and starts a blocking read
loop, stealing all terminal input from the normal input handler.
Minimal reproduction
! cat /dev/tty &
- Note your tty (run
ttyin the shell) for cleanup later - Start
claude - Type:
! cat /dev/tty &
- May need to repeat. In ten trials, never required more than 16 attempts.
- Subprocess fails with "No such device or address" (expected - setsid has no controlling terminal)
- ~2 seconds later, Claude freezes completely
- No input works (typing, Ctrl+C, Ctrl+Z, Ctrl+\)
- Requires
kill -9from another terminal:
``bash``
# Replace pts/42 with your tty from step 1
ps -efly | grep 'pts/42.*claude$' | awk '{print $3}' | xargs -tr kill -KILL
Root cause (from strace)
After the shell command, Claude Code itself opens /dev/tty and starts reading from it:
14:09:54.874 read(22</dev/pts/42>) "! cat /dev/tty &" # User types command
14:10:02.087 clone() = 18143 # Subprocess spawned
14:10:02.557 --- SIGCHLD pid=18143 exited --- # Subprocess exits
14:10:02.563 openat("/tmp/claude-*-cwd") = 24 # Reads cwd temp file
14:10:03.616 read(22</dev/pts/42>) "j" # User types test input
14:10:04.265 read(22</dev/pts/42>) "j" # Last normal pty read
14:10:04.285 access("/dev/tty", F_OK) = 0 # ** PROBE **
14:10:04.286 openat("/dev/tty", O_RDONLY) = 24 # ** ROGUE READER **
14:10:04.286 read(24</dev/tty>) "i" # Steals input
14:10:04.324 read(24</dev/tty>) "j" # ...
...
14:10:05.726 read(24</dev/tty>) <unfinished> # Blocked forever
14:10:27.601 +++ killed by SIGKILL +++
(Full strace available on request.)
The /dev/tty device (major 5, minor 0) resolves to the controlling terminal. This creates a second file descriptor to the same pty:
- fd 22 →
/dev/pts/42(original input handler) - fd 24 →
/dev/tty→/dev/pts/42(rogue reader)
The rogue reader wins the race and consumes all input bytes, starving the normal handler.
Additional observations
Suspected secondary trigger: Multiple hangs occurred while analyzing this bug.
The trigger appears to be output containing the text /dev/tty, not commands
referencing it. Examples of commands that triggered hangs:
grep -Ev '...' strace.log | head -100 # output contained the path
grep -Ev '...' strace.log | tail -50 # output contained the path
The commands themselves had no reference to that device—only the displayed output did.
This suggests Claude Code parses terminal output for paths and proactively accesses
them, triggering the same spurious open. Observed 4+ times during a single analysis
session.
Environment
- Claude Code v2.0.57
- Platform: Linux (Crostini/ChromeOS)
- Kernel: 6.6.x
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗