Skip to content

mgrep watch processes leak on unclean session end — 600+ orphans accumulated, exhausted memory/swap #149

Description

@kankunnawat

Summary

The Claude Code plugin spawns a detached mgrep watch per session (SessionStart hook, preexec_fn=os.setsid) and relies solely on the SessionEnd hook to kill it. SessionEnd only fires when a session terminates cleanly. Any session that is killed instead — headless claude -p invocations driven by other tools, crashes, SIGKILL — never fires it, and the watcher (already detached into its own session group) orphans to launchd and runs forever.

Real-world impact

A menu-bar app on my machine polls Claude usage via headless CLI sessions (~170/day) and kills the process when done. Each probe leaked one watcher. Over two weeks: 606 orphaned mgrep watch processes (~13 MB RSS each, ≈8 GB total), swap 27.5 GB / 28.6 GB full, load average past 900, machine unusable. All orphans had PPID 1 and start dates spanning the whole two weeks.

Diagnosis/cleanup for anyone else hitting this:

ps -axo pid,ppid,command | awk '$2==1 && /mgrep watch/ {print $1}' | xargs kill

Environment

  • mgrep 0.1.10, Claude Code plugin (Mixedbread-Grep marketplace), macOS, Claude Code 2.1.175

Suggested fixes

  • The watcher should not depend on an external kill: pass the spawning session's PID (or the hook's parent claude PID) into mgrep watch and have it self-exit when that process dies.
  • Dedupe per watched directory instead of per session_id — N sessions in the same cwd currently spawn N identical watchers indexing the same tree.
  • A startup sweep in the SessionStart hook that reaps orphaned watchers (PPID 1, same binary) would also cap accumulation.

Related: #134 touches the same PID-file lifecycle.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions