Telegram plugin crashes with 409 error due to orphaned polling processes on session restart

Resolved 💬 2 comments Opened Mar 24, 2026 by davidfpython Closed Apr 22, 2026

Summary

The Telegram channel plugin (grammy-based long polling) crashes with a 409 Conflict error when a new Claude Code session starts, because the previous bot instance's getUpdates polling loop is still running. This means the Telegram channel becomes unreachable after every session restart or /clear.

Steps to reproduce

  1. Enable the Telegram plugin (telegram@claude-plugins-official) in settings.json
  2. Start a Claude Code session — Telegram bot starts polling successfully
  3. Start a new session (or /clear) — Claude Code launches a new Telegram MCP server
  4. The new instance crashes immediately with a 409 error because the old instance is still polling

Error from ~/.claude/channels/telegram/server.log

GrammyError: Call to 'getUpdates' failed! (409: Conflict: terminated by other getUpdates request; make sure that only one bot instance is running)
     method: "getUpdates",
         ok: false,
 error_code: 409,
 description: "Conflict: terminated by other getUpdates request; make sure that only one bot instance is running",

Root cause

The Telegram plugin uses long polling via grammy's bot.start(). The server has a cleanup handler:

process.stdin.on('end', () => {
  void bot.stop()
  process.exit(0)
})

But when Claude Code restarts MCP servers (e.g., on new session or /clear), stdin doesn't always close cleanly, leaving the old bun process as an orphan that continues polling. The new instance then fails because Telegram's Bot API only allows one getUpdates consumer per token.

Evidence

  • Multiple orphaned bun run --cwd .../telegram/0.0.1 processes visible in ps aux after session restarts
  • Similarly, multiple Discord plugin processes accumulate (observed 2 concurrent Discord bun processes with different start times), suggesting this is a general plugin lifecycle issue — but Discord uses WebSocket (not long polling), so it doesn't crash the same way
  • The Telegram plugin is the only one that breaks visibly because of Telegram's strict single-consumer constraint on getUpdates

Expected behavior

When Claude Code starts a new MCP server instance for a plugin, it should:

  1. Terminate the previous process cleanly (SIGTERM → wait → SIGKILL if needed)
  2. Or: the plugin itself should handle the conflict gracefully (e.g., retry after a short delay, or send deleteWebhook(drop_pending_updates=true) before starting polling)

Environment

  • macOS (Apple Silicon)
  • Claude Code with Telegram plugin v0.0.1
  • Bun v1.3.11
  • grammy (Telegram bot framework)

Workaround

Manually kill orphaned Telegram processes before starting a new session:

pkill -f "telegram/0.0.1"

View original on GitHub ↗

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