[telegram plugin] Orphaned `bun server.ts` poller spins at 100% CPU indefinitely; self-termination guards all fail

Open 💬 0 comments Opened Jul 3, 2026 by huni0306

Environment

  • Plugin: telegram@claude-plugins-official v0.0.6
  • Runtime: Bun, macOS (Darwin 25.5.0)
  • Path: ~/.claude/plugins/cache/claude-plugins-official/telegram/0.0.6/server.ts

Summary

When the Claude Code session that launched the telegram MCP server dies uncleanly,
the poller becomes an orphan and pins one CPU core at ~100% indefinitely
(observed: 528 min accumulated on a single PID). It recurs across sessions.

Root cause (verified by inspection)

The process tree is TWO levels: a wrapper bun run --cwd .../telegram/0.0.6 start
spawns the actual bun server.ts. When the launching session dies, the WRAPPER is
reparented to PID 1 but stays alive — so the grandchild server.ts keeps an unchanged
PPID and a live stdin. This defeats all three self-termination guards:

  1. Orphan watchdog process.ppid !== bootPpid — never trips (direct parent unchanged).
  2. stdin end/close handlers — stdin still connected to the live wrapper.
  3. PID-file reaper — sends SIGTERM only; the busy process never processes it.

Additionally, shutdown() relies on bot.stop() + setTimeout(exit, 2000), both of
which require event-loop progress. Because the process is CPU-bound, the timer never
fires, so even a delivered SIGTERM cannot terminate it.

NOTE: the existing 409-Conflict backoff (delay = min(1000*attempt, 15000), bail at 8)
is NOT the spin source — with backoff present the poller would idle, not burn CPU.
The actual busy-loop is something that fully blocks the event loop and was not
captured (process was killed before a stack sample). A sample <pid> from a live
occurrence would pinpoint it.

Impact

  • Silent, permanent 100% CPU per orphan; multiple accumulate over days.
  • Only reliable cleanup is manual kill -9.

Suggested fixes

  1. Eliminate the wrapper layer (use exec bun server.ts) so PPID/stdin death detection works again.
  2. Reaper: escalate SIGTERM → SIGKILL after a short grace period.
  3. On orphan/duplicate detection, do a synchronous process.exit() instead of a timer-based shutdown.
  4. Watchdog: inspect the full ancestor chain to PID 1, not just direct PPID.

Workaround

Disable the plugin (enabledPlugins."telegram@claude-plugins-official": false) if unused.

View original on GitHub ↗