Hang: spurious /dev/tty reader

Resolved 💬 3 comments Opened Dec 10, 2025 by bukzor Closed Dec 14, 2025

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 &
  1. Note your tty (run tty in the shell) for cleanup later
  2. Start claude
  3. Type: ! cat /dev/tty &
  • May need to repeat. In ten trials, never required more than 16 attempts.
  1. Subprocess fails with "No such device or address" (expected - setsid has no controlling terminal)
  2. ~2 seconds later, Claude freezes completely
  3. No input works (typing, Ctrl+C, Ctrl+Z, Ctrl+\)
  4. Requires kill -9 from 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

View original on GitHub ↗

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