Telegram channel plugin: silent polling death on network drop

Resolved 💬 3 comments Opened Mar 20, 2026 by christian-gheorghe Closed Mar 24, 2026

The Telegram channel plugin for Claude Code has a silent-death bug — grammY's bot.start() has no .catch() handler, so when the polling loop drops (network blip, API timeout), the process stays alive but stops receiving messages. No error, no reconnect, no log. Three-line fix: wrap in a retry with backoff.

Repro

  1. Start Claude with --channels plugin:telegram@claude-plugins-official
  2. Wait for a network interruption (or just enough time — long-polling connections drop)
  3. The bun process stays alive (visible in ps) but has zero TCP connections (lsof -i -p <pid> returns nothing)
  4. Bot stops receiving Telegram messages silently

Root cause

server.ts line 594:

void bot.start({
  onStart: info => {
    botUsername = info.username
    process.stderr.write(`telegram channel: polling as @${info.username}\n`)
  },
})

No .catch(), no bot.catch(). When the polling promise rejects, it's a fire-and-forget void — the error vanishes and polling stops permanently.

Fix

function startPolling() {
  bot.start({
    onStart: info => {
      botUsername = info.username
      process.stderr.write(`telegram channel: polling as @${info.username}\n`)
    },
  }).catch(err => {
    process.stderr.write(`telegram channel: polling died — ${err?.message ?? err}\n`)
    process.stderr.write(`telegram channel: restarting polling in 5s\n`)
    setTimeout(startPolling, 5000)
  })
}

bot.catch(err => {
  process.stderr.write(`telegram channel: bot error — ${err?.message ?? err}\n`)
})

startPolling()

Additional note

The Claude harness does not auto-respawn dead MCP plugin child processes. So once the polling loop dies, the only recovery is restarting the entire Claude session. The reconnect patch above keeps the bot alive without requiring a session restart.

View original on GitHub ↗

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