Summary
Outbound Telegram notifications have no retry/backoff for 429 rate-limit errors. A failed send is logged once and silently dropped — the bot's entire value is reliable delivery, and this gap directly undermines that.
Background
sendMessage/notifyChannel in src/telegram/client.ts:28-48 call bot.api.sendMessage() directly with no retry wrapper. grammy's own withRetries() helper (which handles 429 + retry_after backoff) is only applied internally to getMe() and webhook/polling setup — never to arbitrary bot.api.* calls like sendMessage (confirmed in node_modules/grammy/out/bot.js:477-520). The @grammyjs/auto-retry plugin is not a dependency (package.json).
A failed send (429, network blip) propagates up through @octokit/webhooks's handler-error path, is logged once by webhooks.onError (src/github/webhooks.ts), and the event is silently dropped — no retry, no alert.
Evidence: src/telegram/client.ts:35 (await bot.api.sendMessage(...) — no try/catch, no retry); node_modules/grammy/out/bot.js:477-520 (withRetries exists but is opt-in/internal-only); package.json (no @grammyjs/auto-retry).
Impact: this is an ops-notification bot — its entire value is reliable delivery. The active epic (#62) expanding configurable delivery across all event types is actively increasing notification volume and destinations, which increases exposure to Telegram's per-chat (~1 msg/sec) and global rate limits during bursts (bulk-closing issues, CI storms). Silent drops mean the team misses exactly the events (deploy failures, security alerts) the bot exists to surface.
Proposed Solution
Add @grammyjs/auto-retry as a transformer on bot.api, or wrap sendMessage/notifyChannel in a bounded retry loop that honors error.parameters.retry_after from grammy's GrammyError.
Technical Scope
src/telegram/client.ts — sendMessage, notifyChannel
package.json — new dependency (@grammyjs/auto-retry) if that route is chosen
- Tests
Acceptance Criteria
Edge Cases
- 429 with no
retry_after header/parameter present (fall back to a sane default backoff)
- Persistent rate limiting exceeding the retry ceiling (must still surface a clear error/log, not fail silently)
- Concurrent sends to multiple chats during the same burst
Risks
- Low — additive change to the send path; the main risk is an unbounded retry loop blocking event processing, which the bounded-ceiling acceptance criterion is meant to prevent
Deliverables
- Retry-wrapped (or transformer-wrapped) send path
- Unit test mocking
bot.api.sendMessage to throw a GrammyError with error_code: 429 and retry_after, asserting a retry occurs and eventually resolves
Priority
Medium — real reliability gap on the bot's core function, with increasing exposure as the active delivery-expansion epic (#62) ships.
GrantFox Evaluation
Impact Score: 68/100
Difficulty Score: 35/100
Priority Score: 58
Confidence: 85%
Category: Reliability
Estimated Reward Tier: B
AI Rationale: A concrete gap in the bot's single core function (reliable delivery), evidenced directly against the grammy library's actual retry-wrapping behavior, with increasing real-world exposure as an active feature epic expands notification volume. Well-scoped, low-risk fix.
Estimated Completion: 96 hours
Telegram: https://t.me/txioCommunity
Summary
Outbound Telegram notifications have no retry/backoff for 429 rate-limit errors. A failed send is logged once and silently dropped — the bot's entire value is reliable delivery, and this gap directly undermines that.
Background
sendMessage/notifyChannelinsrc/telegram/client.ts:28-48callbot.api.sendMessage()directly with no retry wrapper. grammy's ownwithRetries()helper (which handles 429 +retry_afterbackoff) is only applied internally togetMe()and webhook/polling setup — never to arbitrarybot.api.*calls likesendMessage(confirmed innode_modules/grammy/out/bot.js:477-520). The@grammyjs/auto-retryplugin is not a dependency (package.json).A failed send (429, network blip) propagates up through
@octokit/webhooks's handler-error path, is logged once bywebhooks.onError(src/github/webhooks.ts), and the event is silently dropped — no retry, no alert.Evidence:
src/telegram/client.ts:35(await bot.api.sendMessage(...)— no try/catch, no retry);node_modules/grammy/out/bot.js:477-520(withRetriesexists but is opt-in/internal-only);package.json(no@grammyjs/auto-retry).Impact: this is an ops-notification bot — its entire value is reliable delivery. The active epic (#62) expanding configurable delivery across all event types is actively increasing notification volume and destinations, which increases exposure to Telegram's per-chat (~1 msg/sec) and global rate limits during bursts (bulk-closing issues, CI storms). Silent drops mean the team misses exactly the events (deploy failures, security alerts) the bot exists to surface.
Proposed Solution
Add
@grammyjs/auto-retryas a transformer onbot.api, or wrapsendMessage/notifyChannelin a bounded retry loop that honorserror.parameters.retry_afterfrom grammy'sGrammyError.Technical Scope
src/telegram/client.ts—sendMessage,notifyChannelpackage.json— new dependency (@grammyjs/auto-retry) if that route is chosenAcceptance Criteria
retry_afterresults in the message being retried and eventually delivered rather than droppedEdge Cases
retry_afterheader/parameter present (fall back to a sane default backoff)Risks
Deliverables
bot.api.sendMessageto throw aGrammyErrorwitherror_code: 429andretry_after, asserting a retry occurs and eventually resolvesPriority
Medium — real reliability gap on the bot's core function, with increasing exposure as the active delivery-expansion epic (#62) ships.
GrantFox Evaluation
Impact Score: 68/100
Difficulty Score: 35/100
Priority Score: 58
Confidence: 85%
Category: Reliability
Estimated Reward Tier: B
AI Rationale: A concrete gap in the bot's single core function (reliable delivery), evidenced directly against the grammy library's actual retry-wrapping behavior, with increasing real-world exposure as an active feature epic expands notification volume. Well-scoped, low-risk fix.
Estimated Completion: 96 hours
Telegram: https://t.me/txioCommunity