Telegram plugin: polling exits permanently on network errors during long sessions

Resolved 💬 3 comments Opened Mar 29, 2026 by SeraphKc Closed Apr 2, 2026

Description

The Telegram channel plugin (v0.0.4) stops receiving inbound messages after several hours in a long-running session. Outbound messages (via the reply tool) continue to work, but the bot no longer receives any incoming Telegram messages.

Root Cause

In server.ts, the polling loop (lines ~959-995) only retries on GrammyError with error_code === 409 (conflict). Any other error — including network timeouts, ECONNRESET, ETIMEDOUT, rate limits (429), etc. — causes the loop to return permanently:

// Current behavior (line 991-992):
process.stderr.write(`telegram channel: polling failed: ${err}\n`)
return  // <-- exits the loop forever, no retry

During long sessions, the long-poll connection silently drops due to ISP/firewall idle timeouts, and the polling loop exits without attempting reconnection.

Expected Behavior

Network errors and rate limits should be retried with exponential backoff, similar to the existing 409 handling. Only truly non-retryable errors (like invalid token) should exit the loop.

Suggested Fix

Add a check for retryable errors before the final return:

const errMsg = String(err)
const isNetworkError = /ECONNRESET|ETIMEDOUT|ENOTFOUND|EHOSTUNREACH|EAI_AGAIN|socket hang up|network|timeout|fetch failed/i.test(errMsg)
const isRateLimited = err instanceof GrammyError && (err.error_code === 429 || err.error_code === 408)

if (isNetworkError || isRateLimited) {
  const delay = Math.min(5000 * attempt, 60000)
  process.stderr.write(`telegram channel: polling lost connection, reconnecting in ${delay / 1000}s\n`)
  await new Promise(r => setTimeout(r, delay))
  continue
}

Environment

  • Claude Code version: 2.1.85
  • Plugin version: 0.0.4
  • Grammy version: 1.41.1
  • OS: Ubuntu 25.04
  • Session duration when issue occurs: 4-8+ hours

Workaround

Restarting the Claude Code session re-establishes the polling connection. Using systemd with Restart=on-failure helps for daemon-mode agents.

View original on GitHub ↗

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