macOS: pty FD leak in Claude Desktop Electron parent — kern.tty.ptmx_max exhaustion blocks new terminals

Resolved 💬 3 comments Opened May 22, 2026 by iamjohnnymac Closed May 22, 2026

Summary

Claude Desktop's Electron parent process leaks /dev/ptmx file descriptors over time. On a busy day of normal use, it accumulates enough open ptys to hit macOS's hard kern.tty.ptmx_max ceiling (511 on macOS 26.4.1), at which point no new pseudo-terminal can be allocated anywhere on the system — new Ghostty/Terminal windows fail, xcodebuild test fails with Pseudo Terminal Setup Error, new SSH connections may hang.

The only known workaround is Quit + relaunch Claude Desktop.

Environment

  • macOS 26.4.1 (build 25E253), Apple Silicon
  • Claude Desktop version 1.8089.1
  • Claude Code version 2.1.142
  • Several Claude Code sessions resumed throughout the day (some via --resume <uuid> flag)
  • Multiple MCP servers configured (chrome-devtools, elevenlabs)
  • Multiple plugins loaded (skills-plugin, swift-lsp, frontend-design)

Reproduce

I don't have a deterministic minimal repro, but the conditions when it triggered:

  1. Open Claude Desktop, work through several Claude Code sessions over ~12+ hours
  2. Each session involves many Bash tool calls, MCP server spawns, sub-agents, and Chrome MCP work
  3. After enough activity, Ghostty.app refuses to open new windows with "Your system cannot allocate any more pty devices"

Quitting and relaunching Claude Desktop drops the pty count to ~0 immediately.

Evidence

\\\`console
$ sysctl kern.tty.ptmx_max
kern.tty.ptmx_max: 511

$ ls /dev/ttys??? | wc -l
511

$ lsof 2>/dev/null | awk '/\/dev\/(ttys|ptmx)/' | wc -l
511

$ lsof 2>/dev/null | awk '/\/dev\/(ttys|ptmx)/ {print \$1}' | sort | uniq -c | sort -rn | head
511 Claude

$ lsof 2>/dev/null | awk '\$1==\"Claude\" && /\/dev\/ptmx/ {print \$2}' | sort -u
3917

$ ps -p 3917 -o command=
/Applications/Claude.app/Contents/MacOS/Claude
\\\`

A single PID (\3917\, the Electron parent) holds all 511 \/dev/ptmx\ FDs. Sample lines:

\\\
Claude 3917 user 41u CHR 15,1 0t0 605 /dev/ptmx
Claude 3917 user 48u CHR 15,2 0t0 605 /dev/ptmx
Claude 3917 user 70u CHR 15,148 0t0 605 /dev/ptmx

\
\\

For comparison, my other Mac (a clean MacBook Pro on the same Tailnet running only the chrome-extension bridge of Claude — no full Claude Desktop sessions) was at 0/511 ptys in use at the same time.

Expected

Pty FDs allocated for short-lived child processes (Bash tool calls, MCP server spawns, transient sub-agents) should be closed when the child exits, not retained by the Electron parent.

Actual

Pty FDs are retained by the parent across the lifetime of the Claude Desktop process. Over many hours of normal use, they accumulate to the macOS hard cap.

Workaround

Quit + relaunch Claude Desktop. This unfortunately terminates all running Code sessions — \/resume <uuid>\ is needed after.

Suspected cause (speculative)

Child processes spawned with a pty are likely keeping the master FD open on the parent side after the child exits, possibly due to a missing \close()\ after \posix_spawn\/\fork\+\exec\ returns or after the child detaches. Each Bash tool call appears to consume one FD that's never released.

View original on GitHub ↗

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