PTY leak: Bash tool leaks master PTY file descriptors, exhausting kern.tty.ptmx_max
Open 💬 0 comments Opened Jun 23, 2026 by fdebane
Bug
Each Bash tool invocation opens a PTY master (/dev/ptmx) but never closes the file descriptor after the subprocess exits. Over a long session, leaked FDs accumulate until the kernel's kern.tty.ptmx_max pool is exhausted. At that point, Terminal.app (and any other app calling forkpty) fails with:
[forkpty: Device not configured]
[Could not create a new process and open a pseudo-tty.]
Reproduction
Run a Claude Code desktop session with many Bash tool calls, then:
lsof /dev/ptmx | grep Claude | wc -l
Evidence (this session)
- Claude PID 87601 held 518 open
/dev/ptmxfile descriptors kern.tty.ptmx_max= 511 → pool fully exhausted- 527
/dev/ttys*devices present vs 511 max sudo sysctl -w kern.tty.ptmx_max=...itself failed (also needs PTY)- Workaround:
osascript -e 'do shell script "sysctl -w kern.tty.ptmx_max=600" with administrator privileges'
lsof /dev/ptmx output (truncated):
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
Claude 87601 franck 42u CHR 15,7 0t0 605 /dev/ptmx
Claude 87601 franck 46u CHR 15,4 0t0 605 /dev/ptmx
Claude 87601 franck 73u CHR 15,9 0t0 605 /dev/ptmx
... (518 total entries for PID 87601)
Environment
- Claude Code: 2.1.119
- macOS: 26.5.1 (Darwin 25.5.0, Build 25F80)
- Hardware: MacBook M2
Fix
Close the PTY master FD in the Bash tool after the child process exits. The master should be closed once the subprocess's stdout/stderr are fully drained and the process has been wait()ed.
Impact
Terminal.app becomes unusable mid-session. Workaround requires admin privileges or a full restart of Claude Code.