macOS: long-running session leaks /dev/ptmx masters, exhausts kern.tty.ptmx_max (forkpty ENXIO system-wide)

Resolved 💬 2 comments Opened Jun 18, 2026 by Nickcom4 Closed Jun 22, 2026

Summary

A long-running Claude Code CLI session on macOS accumulates a large number of /dev/ptmx master pseudo-terminals held open by its process, without releasing them. Because macOS caps the total system-wide pty count (kern.tty.ptmx_max, default 511), a single long-lived session can consume a large fraction of the pool, and once the aggregate hits the cap every forkpty() on the machine fails with ENXIO ("Device not configured") — new Terminal/kitty windows and any subprocess that needs a pty stop opening, system-wide.

Observed (verified)

On a session that had been running a while:

$ lsof /dev/ptmx | wc -l
101
# ranked holders:
101  Claude  <pid>      # one Claude Code process holding 101 ptmx masters
$ sysctl kern.tty.ptmx_max
kern.tty.ptmx_max: 511

One Claude Code process held 101 open /dev/ptmx masters. The pool sat at 117/511; under more concurrent sessions (or a longer-lived one) this reaches 511 and forkpty: Device not configured follows for everything on the system.

Likely cause (inference, not confirmed from source)

It looks like pty masters allocated for shell / Bash-tool / subprocess execution are not being closed/reaped promptly after each command completes, so the count grows monotonically over a session's lifetime rather than staying proportional to concurrent in-flight commands. ~100 masters for one session is far above what concurrent execution should need.

Repro (expected)

  1. macOS (default kern.tty.ptmx_max=511).
  2. Run a Claude Code session that executes many Bash/tool commands over an extended period (and/or several concurrent sessions).
  3. Watch lsof /dev/ptmx | awk '{print $1,$2}' | sort | uniq -c | sort -rn — the Claude process's master count climbs and does not fall back as commands finish.
  4. Eventually forkpty() returns ENXIO and new shells/terminals fail until sessions are quit / logout / reboot.

Impact

System-wide: once the pool is exhausted, Terminal, kitty, and any pty-needing process fail to start — not just Claude Code. Recovery today is to quit idle Claude sessions, log out, or reboot.

Ask

  • Does Claude Code close pty master fds after each subprocess/Bash-tool invocation completes? An accumulation to ~100 per session suggests masters are retained for the session lifetime.
  • If retention is intended, consider reaping on command completion, or bounding the per-session master count.

Environment

  • OS: macOS (Darwin 25.5.0)
  • Claude Code CLI (Opus 4.x)
  • kern.tty.ptmx_max default 511; macOS hard max is 999 (PTMX_MAX_HARD, XNU bsd/kern/tty_ptmx.c).

Workaround (bridge only, not a fix)

Raise the cap: sudo sysctl -w kern.tty.ptmx_max=999 (immediate; resets on reboot; 999 is the kernel hard max). This only buys headroom — it does not stop the accumulation.

View original on GitHub ↗

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