Skip to content

ptyhost leaks one pty master fd per spawned shell — exhausts macOS 511-pty limit and breaks all terminals system-wide (forkpty: Device not configured) #86

Description

@likunhong01

环境

  • Qoder 1.27.1(macOS arm64)
  • macOS 25.5.0
  • ptyhost 进程(Qoder Helper,node-pty)连续运行约 22 小时

现象

系统 Terminal.app 与 VS Code 集成终端均无法新建窗口/标签页:

[forkpty: Device not configured]
[未能创建新的进程和打开伪终端(pseudo-tty)。]

只有重启 Mac 或彻底退出 Qoder 才能恢复,之后继续使用会复发。

根因

macOS 内核 pty 上限为 511(kern.tty.ptmx_max)。ptyhost 每次 spawn 一个 shell 进程就分配一个 pty,子进程正常退出后 master fd 从不关闭,持续累积直到占满全表:

# pty 表已满:动态节点数 = 内核上限
$ ls /dev/ttys[0-9a-f][0-9a-f][0-9a-f] | wc -l
511

# 全部 511 个 master fd 均由 ptyhost 持有
$ lsof /dev/ptmx | awk 'NR>1 {print $1, $2}' | sort | uniq -c | sort -rn
511 Qoder 27685

$ lsof -p 27685 | grep -c /dev/ptmx
511

关键证据:日志与泄漏数严格对应

~/Library/Application Support/Qoder/logs/<session>/ptyhost.log 中共 510 条 Process exited 记录(id 1–511,全部 exitCode: 0),加上当前活跃的 1 个会话 = 511,与泄漏的 master fd 数完全一致:

2026-08-31 12:21:26.124 [info] [PtyService] Initializing, pid: 27685, graceTime: 60000
2026-08-31 12:21:32.200 [info] [PtyService] Process exited, id: 1, exitCode: 0
...
2026-09-01 10:20:10.734 [info] [PtyService] Process exited, id: 511, exitCode: 0

即:每一个已退出的 shell 进程,其 master fd 都没有被释放。泄漏速率与使用强度一致(重度使用 Agent 的 Bash 工具时约每小时 30–66 个,夜间闲置时为 0),一个工作日左右即填满 511 上限,导致整机终端不可用。

复现步骤

  1. 启动 Qoder,正常使用会 spawn shell 的功能(Agent 执行 Bash 命令 / 打开关闭集成终端)
  2. 观察 master fd 数单调增长,进程退出后不下降:
    lsof -p <ptyhost_pid> | grep -c /dev/ptmx
  3. 累计到 511 后,系统所有应用的 forkpty 均失败

期望行为

子进程退出后应关闭对应的 master fd。推测是 node-pty 的 exit/close 路径未释放句柄,或 terminal session 对象未被 dispose/GC。

其他

ptyhost 启动时还有一条报错,不确定是否与泄漏相关:

[error] Failed to set sticky bit on /private/var/folders/.../T/<user>-qoder-zsh: ENOENT: no such file or directory, chmod '...'

临时规避

彻底退出 Qoder(⌘Q)即可释放全部 master fd,无需重启系统。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions