[BUG] Channel plugin bun processes orphan on session exit, accumulate indefinitely
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
- Install a plugin that ships a channel (the official
imessageplugin at.claude/plugins/cache/claude-plugins-official/imessage/0.1.0is one), OR load a development channel with--dangerously-load-development-channels server:<name>. - Start a Claude Code session:
\\\\
claude --dangerously-skip-permissions --channels plugin:imessage@claude-plugins-official
\\
- 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.
- Exit the Claude session (Ctrl+C,
/exit, or kill the parent process). - 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).
- 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
- Parent-death watcher in the bun channel server template — poll
process.ppidor usePR_SET_PDEATHSIGequivalent, exit when parent becomes 1. - Explicit teardown in Claude Code session exit path — track channel PIDs at spawn time, SIGTERM-then-SIGKILL them on exit.
- 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.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗