[BUG] Channel plugin bun processes orphan on session exit, accumulate indefinitely

Resolved 💬 3 comments Opened Apr 20, 2026 by jared148 Closed May 27, 2026

What's Wrong

When a Claude Code session uses channel plugins (e.g. the official imessage plugin, or custom channels loaded via --dangerously-load-development-channels), the bun server.ts processes spawned for those channels are not killed when the parent session exits. They get reparented to PID 1 and continue running indefinitely, each consuming 150–200 MB of RSS.

Over days of normal usage (starting/ending sessions), dozens of these orphans accumulate. On a 24 GB system I hit memory exhaustion with ~64 GB of swap in use, forcing a hard reboot.

After a full manual cleanup on 2026-04-16, the same buildup recurred within 4 days, so this is fully reproducible rather than a one-off.

What Should Happen

When a Claude Code session exits (normal exit, crash, or parent Claude Desktop quit), its spawned channel bun server.ts children should be terminated. Either the child should detect parent death and self-exit, or Claude Code should send SIGTERM/SIGKILL to its channel children before exiting.

Error Messages / Logs

Process snapshot before cleanup on 2026-04-20 (26 of 31 total bun processes are orphans, PPID=1):

\\\`
$ ps -eo pid,ppid,rss,etime,command | awk '\$2==1 && /bun.*server\.ts/' | head -10
59453 1 207376 02-01:05:20 /Users/jaredmeurer/.bun/bin/bun server.ts
76873 1 206320 01-02:06:01 /Users/jaredmeurer/.bun/bin/bun server.ts
79028 1 197776 01-02:04:50 /Users/jaredmeurer/.bun/bin/bun server.ts
57211 1 192272 02-01:06:45 /Users/jaredmeurer/.bun/bin/bun server.ts
77825 1 190784 01-02:05:27 /Users/jaredmeurer/.bun/bin/bun server.ts
...

Total orphaned bun processes: 26
Aggregate RSS of orphans: 4,251 MB
System swap usage at time of cleanup: 6,474 MB / 8,192 MB
\\\`

Notable: orphans reliably ignore SIGTERM — every manual cleanup required \kill -9\. This suggests the bun runtime is stuck in a state where signal handlers aren't firing (possibly an event loop stall after the stdio parent disappears).

Observation frequency (three data points): 20 orphans over 5 days, 26 orphans over 4 days — roughly 5–6 new orphans per day of normal usage. This scales linearly with the number of distinct Claude Code sessions started.

Steps to Reproduce

  1. Install a plugin that ships a channel (the official imessage plugin at .claude/plugins/cache/claude-plugins-official/imessage/0.1.0 is one), OR load a development channel with --dangerously-load-development-channels server:<name>.
  2. Start a Claude Code session:

\\\
claude --dangerously-skip-permissions --channels plugin:imessage@claude-plugins-official
\
\\

  1. Verify the channel bun server is running:

\\\
ps -ef | grep 'bun.*server.ts'
\
\\
You'll see a bun server.ts process whose PPID is the claude process, and a bun run --cwd .../imessage shell parent.

  1. Exit the Claude session (Ctrl+C, /exit, or kill the parent process).
  2. Check the bun processes again:

\\\
ps -eo pid,ppid,command | awk '\$2==1 && /bun.*server\.ts/'
\
\\
The bun server.ts is still running, now with PPID=1. It will stay there until the machine reboots or you kill it manually with SIGKILL (SIGTERM does not work).

  1. Repeat steps 2–4 a few times over a day. Observe orphan count growing monotonically.

Environment

  • OS: macOS 26.4.1 (build 25E253), Apple M4, 24 GB RAM
  • Claude Code CLI: 2.1.111
  • Claude Desktop: 1.3109.0
  • bun: 1.3.11
  • Channels in use: imessage (official plugin, v0.1.0) and a custom development channel (rodney-events)

Workaround

Hourly launchd job that SIGKILLs any bun server.ts with PPID=1. Script + plist: rodney@01b1fa53. This stops the accumulation but doesn't fix the root cause.

Suggested Fix Directions

  1. Parent-death watcher in the bun channel server template — poll process.ppid or use PR_SET_PDEATHSIG equivalent, exit when parent becomes 1.
  2. Explicit teardown in Claude Code session exit path — track channel PIDs at spawn time, SIGTERM-then-SIGKILL them on exit.
  3. Both — defense in depth, since neither alone handles all exit paths (e.g. OOM kill of the parent).

Option 1 is self-contained in the plugin template and would fix all plugins that follow that template. Option 2 fixes custom channels too.

I'd be happy to contribute a PR for option 1 if it'd be useful — the fix is ~10 lines in the channel server template.

View original on GitHub ↗

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