[BUG] Claude desktop app leaks pseudo-terminal (pty) master fds, exhausting macOS `kern.tty.ptmx_max` and locking out all terminal apps

Resolved 💬 1 comment Opened Jun 14, 2026 by ysam21 Closed Jun 22, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Summary

The Claude desktop app opens a pseudo-terminal master (/dev/ptmx) for shell/agent
operations and never closes it. The master fds accumulate for the lifetime of the app
process. After a few days of normal use they exhaust macOS's system-wide pty limit
(kern.tty.ptmx_max, default 511), at which point no application on the machine can open
a new terminal
— Terminal.app and iTerm2 windows open and immediately die because the
kernel can't allocate a tty.

Impact / severity — High

This is not contained to Claude. Once the cap is hit, any app needing a pty fails
system-wide (Terminal.app, iTerm2, VS Code/Cursor integrated terminals, ssh sessions, etc.).
The failure is silent and confusing — the apps look "broken" with no error and no crash log.
Recovery requires quitting the Claude app or rebooting. Users are unlikely to connect their
inability to open a terminal to the Claude desktop app.

Likely root cause

A pty master (probably via node-pty / Electron) is created per shell/agent/Bash/MCP
operation, but the master file descriptor is not closed when the corresponding process ends.
Over a long-lived app session these leak until kern.tty.ptmx_max is exhausted.

Suggested fix

Ensure the pty master fd is closed when its child process exits (close-on-exit for every
spawned shell/agent/MCP pty), and consider an idle-session reaper as a safety net.

What Should Happen?

  • Expected: the pty master is closed when its child shell / agent command / MCP stdio

process exits, returning the slot to the kernel.

  • Actual: the master fd stays open for the lifetime of the app process and accumulates

without bound.

Error Messages/Logs

#### Evidence (from an affected machine)
App process, ~3 days uptime, holding 519 pty masters:

$ ps -p <pid> -o pid=,etime=,comm=
<pid>  03-03:23:59  /Applications/Claude.app/Contents/MacOS/Claude

$ lsof -nP -p <pid> | grep -c /dev/ptmx
519

Attribution across all processes — the app holds ~all allocated ptys, while only ~6 are
backed by a live tty session:

$ lsof -nP | grep /dev/ptmx | awk '{print $1}' | sort | uniq -c | sort -rn | head
 519 Claude
   6 Cursor
   4 zsh   (x several)
   1 Terminal
open /dev/ptmx masters total: 527     distinct slave ttys actually open: 6

Kernel refusing new ptys when the cap is reached:

$ python3 -c 'import os; os.openpty()'
OSError: [Errno 6] Device not configured     # ENXIO — no free pty slots

The count was observed growing live (516 → 519 masters over a handful of tool calls in a
single session), confirming masters are opened per-operation and not released.

Steps to Reproduce

  1. Use the Claude desktop app over several days without quitting it (a long-lived app process).
  2. Run agent/Bash/terminal operations during that time (each appears to open one pty master).
  3. Observe the count of open /dev/ptmx masters held by the app climb by roughly one per

shell operation, ~150–170/day, and never decrease.

  1. Once total allocated ptys reaches kern.tty.ptmx_max (511), Terminal.app and iTerm2 stop

opening — window appears then instantly closes; no crash report is generated.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.170

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Environment
  • Claude desktop app: 1.11847.5
  • Claude Code (running inside it): 2.1.170
  • macOS: 26.1 (build 25B78)
  • kern.tty.ptmx_max: 511 (macOS default; hard max is 999)
Workarounds (for affected users)
  • Immediate, no reboot: fully quit and reopen the Claude desktop app (Cmd-Q) — releases

all leaked masters at once.

  • Temporary headroom: sudo sysctl -w kern.tty.ptmx_max=999 (999 is the kernel max;

values above it return EINVAL). Resets on reboot.

  • A reboot also clears all leaked ptys.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗