WEMAIDE / INSIGHTS / ENGINEERING
Telegram bot not sending messages? Trace the failure before restarting it
Diagnose Telegram Bot API failures by separating incoming updates, application processing, outgoing requests and delivery. Includes a safe troubleshooting record.
When a Telegram bot stops replying, a restart is a guess. First find the boundary that stopped working: did Telegram produce an update, did your application receive it, did the handler finish, and did Telegram accept the outgoing message? A bot that receives nothing needs a different fix from a bot whose sendMessage request fails.
This guide covers the Telegram Bot API and a bot you operate. It is a diagnostic procedure, not a report of a particular client outage. Use a test chat whose participant expects the test, preserve pending work, and keep credentials and personal message content out of shared logs.
A quick first test when the bot is not responding
Use one expected test participant and one deployment. Confirm the bot with getMe, inspect getWebhookInfo, send /start in the intended chat and trace that event to one plain-text reply. Run API calls from your secured server tooling; do not paste a token-bearing URL into the browser or a shared ticket. The fixtures below are synthetic responses, not logs from a client incident.
Synthetic getWebhookInfo response A
{"ok":true,"result":{"url":"","pending_update_count":0}}
Synthetic getWebhookInfo response B
{"ok":true,"result":{
"url":"https://bot.example.invalid/updates",
"pending_update_count":12,
"last_error_message":"Wrong response from the webhook: 502 Bad Gateway"
}}
Synthetic sendMessage result
{"ok":true,"result":{"message_id":42,"chat":{"id":123,"type":"private"},"date":1800000000,"text":"Test"}}| Observed result | Next check | Useful retest evidence |
|---|---|---|
| A: no webhook URL | Verify the intended polling worker is running and no competing process consumes its updates | The new /start update reaches the intended handler |
| B: pending updates plus a recent 502 | Inspect the HTTPS endpoint, proxy and application error at that time; preserve queued work | The delivery error stops recurring and the known event reaches durable intake |
| 409 on getUpdates | Read its description: active webhook and competing poller conflicts require different configuration checks | Only the intended transport/consumer handles the test event |
| 403 on a private-chat send | Check the exact description, bot identity and whether the participant started or blocked this bot | A consenting test participant receives the intended plain reply |
| 429 with parameters.retry_after = 18 | Wait at least 18 seconds, then revalidate the queued operation; this is an example delay | A bounded retry after the delay has a recorded result |
| Successful Message response as above | Compare returned chat/topic with the intended destination | Record message_id; do not infer a read receipt |
An empty webhook URL does not prove polling is healthy, and a historical last_error_message does not by itself prove a current outage. Compare timestamps and the new test event. A timeout without a definitive response remains unknown: it is not the same as the explicit 403 or 429 rejection above.
Start with the symptom, not the deployment
| Symptom | Inspect first | What it separates |
|---|---|---|
| No command reaches your logs | Bot identity, update transport and configured update types | Receiving updates from executing the command |
| The update is logged but there is no reply attempt | Handler state, exceptions, queue and dependencies | Application processing from Telegram delivery |
| The outgoing request returns an error | HTTP status, Bot API error and response parameters | A rejected request from a local timeout |
| API success is recorded but the user sees nothing | Returned chat and message identifiers, topic and notification expectations | API acceptance from reading or receiving a notification |
| Some messages arrive twice | Update identity, business operation and outgoing attempts | Repeated input from repeated side effects |
1. Confirm the bot and the destination
Call getMe from your secured application environment to confirm which bot the configured token identifies. Compare that identity with the bot the tester actually opened. A valid token for a staging bot can produce successful API responses while the operator is watching the production chat. Do not paste a token-bearing Bot API URL into a support ticket, screenshot or analytics log.
For a private-chat test, have the participant open the intended bot and send /start. A bot cannot initiate an arbitrary private conversation with a user who has not contacted it. For a group, channel or forum topic, verify the actual chat identity, membership, required permissions and any message_thread_id your workflow supplies. Do not copy a chat identifier from an unrelated environment.
2. Check webhook or long polling without discarding updates
Use getWebhookInfo to see whether a webhook URL is configured. Inspect pending_update_count and, when present, last_error_date and last_error_message. A rising pending count combined with a delivery error is useful evidence; a zero count alone does not prove that your handler executed successfully. Compare the timestamps with the test event.
getUpdates does not work while an outgoing webhook is configured. Decide which transport this deployment is supposed to use, then check that transport. For polling, look for competing consumers and whether the offset is advanced after the intended processing boundary. For a webhook, check endpoint reachability, the expected route, its response status and your validation of the configured secret header.
| Deployment condition | Reasonable starting point | Still required |
|---|---|---|
| A persistent worker with outbound connectivity | Long polling may be appropriate | Consumer coordination, offset policy and restart recovery |
| A stable public HTTPS endpoint | A webhook may be appropriate | Request validation, durable intake and timely responses |
| Long AI or third-party processing | Either transport with durable queued work | Record intake before acknowledgement; process expensive work separately |
| A host that sleeps or restarts frequently | Evaluate the host lifecycle before choosing | A recovery policy for work left in progress |
Long polling is not inherently a slow periodic check: the server can hold the request until an update is available. Switching to webhooks is therefore not a universal latency fix. Likewise, deleting a webhook, changing the polling offset or using drop_pending_updates can lose work. Make those changes only with a deliberate migration or discard policy, not as a troubleshooting ritual.
3. Trace one update through the application
Create one known test event and follow its update_id through intake, handler selection, validation, queue and outgoing request. Check whether allowed_updates excludes the event type you expect. In groups, privacy mode and the type of message also affect which updates a bot receives; use a supported command addressed to the bot before concluding that transport is broken.
An HTTP success from your webhook endpoint can acknowledge an update even if later application work fails. If the handler starts a slow AI call and then crashes, Telegram transport may be healthy while your own workflow has lost the job. Record a durable work item and observable processing state rather than relying on a single line saying that the webhook returned 200.
4. Try one plain-text reply and inspect the response
Send a short plain-text test through the same application connection and intended test chat, initially without parse_mode, media or optional formatting. This isolates chat access and basic sending from entity parsing, file retrieval and template construction. Then add the failing option back one at a time. A successful minimal test does not validate the original message payload.
| Result | Useful evidence | Next action |
|---|---|---|
| Successful Message response | Returned chat identity and message_id | Record acceptance; inspect the intended destination and topic |
| Bad request or permission error | Exact description and failed operation | Correct the payload or access; do not blindly repeat the same request |
| Flood control | retry_after when returned | Wait at least the specified interval and revalidate queued work |
| Connection timeout without a definitive result | Attempt identity, timestamps and available transport evidence | Mark uncertain; decide whether and how to reconcile before another send |
| Application never attempted sendMessage | Last completed stage and dependency error | Repair processing; changing Telegram permissions will not fix the missing attempt |
A successful Bot API response is not a read receipt and does not promise a notification on the user's device. Muted chats, the wrong destination or a forum topic can explain the difference. Do not rotate a working token or resend a real notification solely because the tester did not see a push alert.
5. If replies are duplicated, identify which operation repeated
Two incoming records with the same update_id suggest repeated intake. Two different updates may represent two separate user actions. One recorded operation with two outgoing attempts points to retry or worker coordination. These cases need different fixes. A durable unique operation identity and an atomic claim help inside your application, but they cannot make Telegram and your database one transaction.
After a timeout, Telegram may already have accepted a message. Retrying without a confirmed failure can duplicate it. Keep an uncertain state and choose the tradeoff for the product: an operator review may be appropriate for an important notification, while a routine digest can have a documented skip policy. Do not promise exactly-once external delivery from a local deduplication key.
Keep a diagnostic record that another person can use
Sanitized incident record — illustrative fields
observed_at_utc | environment | bot_alias | transport
update_id | operation_id | stage | attempt_number
http_status | api_error_category | retry_after_seconds
message_id_if_confirmed | outcome | next_action
Exclude: tokens, secret headers, full private messages and unnecessary user identifiers.Finish with a controlled retest: one incoming command, one application operation, one intended outgoing action and a recorded outcome. Then test a restart, repeated intake and a temporary dependency failure separately. Keep the original evidence so a later intermittent incident can be compared with the same stage boundaries.

Start a project ↗

