Start here: one command tells you which half of the problem you have
Before you change any code, call getWebhookInfo. Its response splits every "bot not responding" problem into two families, and the fix is completely different for each.
Last verified: August 2026 against the Telegram Bot API. Error strings quoted below are the literal responses the API returns.
Open this in a browser, replacing <TOKEN> with your bot token:
https://api.telegram.org/bot<TOKEN>/getWebhookInfoRead the url field in the JSON that comes back:
What url says | What it means | Where to look next |
|---|---|---|
"" (empty) | No webhook set. Your bot must be polling with getUpdates. | Is your process actually running? Is something else polling the same token? |
| A URL you recognise | Webhook mode. Telegram is pushing to that address. | Check last_error_message and pending_update_count in the same response. |
| A URL you do not recognise | An old deploy, a teammate's tunnel, or a stale staging environment is stealing your updates. | Delete it. This is the single most common cause of a silent bot. |
Two more fields in that response are worth more than an hour of debugging:
pending_update_count- if this number is large and climbing, Telegram has updates for you and nothing is collecting them. Your bot is not being ignored by Telegram; your bot is not listening.last_error_message- if this is populated, Telegram tried to deliver and your server rejected it. The message is usually blunt and correct:Connection refused,SSL error,Wrong response from the webhook: 502 Bad Gateway.
The rest of this guide walks the nine causes in rough order of how often they actually turn out to be the answer.
1. The 409 conflict: getUpdates and webhook cannot coexist
The exact error is:
{"ok":false,"error_code":409,"description":"Conflict: can't use getUpdates method while webhook is active; use deleteWebhook to delete the webhook first"}Telegram permits exactly one consumer of updates per bot token. You either poll with getUpdates or you receive pushes at a webhook. Never both. When a webhook is registered and your code starts polling, the poll fails with 409 and your bot goes silent - even though the process is running and the logs may look healthy, because many libraries swallow this error and retry forever.
To switch to polling, remove the webhook first:
https://api.telegram.org/bot<TOKEN>/deleteWebhook?drop_pending_updates=trueThe drop_pending_updates=true parameter matters more than people expect. Without it, every message queued while the bot was down gets delivered the instant you start polling. A bot that was offline for two days will replay two days of conversations at once, sending welcome messages and notifications to people who moved on long ago. Include the flag unless you specifically want the backlog.
The variant nobody expects: two copies of your own bot
A 409 also appears with a different description when two processes poll the same token:
Conflict: terminated by other getUpdates request; make sure that only one bot instance is runningThis is the classic symptom of a bot that responds to roughly half the messages, seemingly at random. Two instances are both long-polling; each message goes to whichever one happened to be holding the connection. Common sources: a local dev process still running while production is live, a container that was scaled to 2 replicas, a nodemon or watchdog restart that left the old process alive, or a free-tier host that spun up a second dyno.
Polling does not scale horizontally. If you need more than one instance, you need webhooks and a load balancer in front of them.
2. Privacy mode: why the bot works in DMs but ignores your group
If your bot replies fine in a one-to-one chat but is completely silent in a group, privacy mode is the cause about nine times out of ten.
By default, a bot added to a group receives only a narrow slice of messages:
- Messages that start with a slash command (
/start,/help) - Messages that explicitly @-mention the bot
- Replies to one of the bot's own messages
- Service messages (people joining, leaving, being promoted)
Everything else - ordinary conversation between members - is never delivered to your bot at all. This is a deliberate privacy protection, not a bug, and it means no amount of code fixing will help. Your handler is correct; the update never arrives.
To turn it off, talk to @BotFather:
/setprivacy -> select your bot -> DisableThen comes the step that trips up most people: the change does not apply to groups the bot is already in. You must remove the bot from the group and re-add it. Until you do, it keeps operating under the privacy setting that was active when it joined.
One alternative is worth knowing: promoting the bot to group admin also grants it visibility of all messages, regardless of the privacy setting. If you cannot re-add the bot for social reasons - a large community that would notice - promoting it to admin achieves the same result.
The command-suffix trap in groups
In a group containing more than one bot, Telegram clients append the bot username to commands: a user tapping /help actually sends /help@YourBotName. Handlers matching the literal string /help will not fire. Match on the command prefix and strip anything after @, or use a framework helper that normalises this for you. This produces the specific and confusing symptom of a bot that works in a test group and fails in the real one.
3. The webhook is set, and quietly failing every delivery
When getWebhookInfo shows a URL plus a populated last_error_message, Telegram is doing its job and your endpoint is refusing the handoff. The messages are terse but accurate. Here is what each one actually means.
last_error_message | Real cause | Fix |
|---|---|---|
| SSL error / certificate verify failed | Expired cert, or a chain missing its intermediate certificate | Test with SSL Labs. Browsers forgive an incomplete chain; Telegram does not. |
| Connection refused | Nothing listening, or a firewall blocking Telegram's IP ranges | Confirm the port is open to the public internet, not just your VPC. |
| Wrong response from the webhook: 502 / 503 | Your app is crashing or cold-starting on each delivery | Check application logs at the timestamp in last_error_date. |
| Read timeout expired | Your handler does slow work before responding | Return 200 immediately, then process on a queue. See below. |
| Wrong response from the webhook: 404 | Path mismatch between registered URL and your route | Compare the url field character by character against your router. |
Four webhook rules that are easy to violate
Telegram is strict about webhook endpoints in ways that other platforms are not:
- HTTPS only. A plain HTTP URL is rejected at registration time. There is no development exception.
- Only four ports. 443, 80, 88 and 8443. A webhook on port 3000 will never be called, and Telegram will not warn you clearly about why.
- Self-signed certificates require an upload. You must supply the public certificate with the
setWebhookcall. A self-signed cert without the upload fails every delivery. - Respond fast, and respond 200. Any non-2xx counts as a failure and triggers a retry with backoff. Retries mean duplicate processing, which is why users sometimes receive the same reply three times.
The pattern that prevents timeout failures
The single most valuable structural change you can make is to stop doing work inside the webhook handler. Acknowledge, then process:
app.post('/telegram-webhook', (req, res) => {
res.sendStatus(200); // acknowledge first, always
queue.push(req.body); // then do the slow part off the request path
});A handler that calls an LLM, queries a database and hits a CRM before responding will exceed Telegram's patience under load. Telegram retries, your bot double-replies, and the failure looks intermittent and impossible to reproduce - because it only happens when the downstream call is slow.
4. 403 Forbidden: the user blocked your bot, or never started it
Two 403 responses look similar and mean different things:
{"ok":false,"error_code":403,"description":"Forbidden: bot was blocked by the user"}
{"ok":false,"error_code":403,"description":"Forbidden: bot can't initiate conversation with a user"}The first is exactly what it says. The user blocked you. There is no recovery path and no API to check block status in advance - the send attempt is the check. The correct response is to mark that chat inactive in your database and stop sending. Bots that keep retrying blocked users burn rate limit on messages that will never arrive.
The second is a rule people meet the first time they try to build a notification system: a Telegram bot can never message someone first. The user must initiate by sending /start, or by tapping a deep link. If you have a phone number or a user ID from elsewhere, that is not enough - Telegram requires the user to opt in inside Telegram itself.
The standard pattern is a deep link that carries your own identifier:
https://t.me/YourBotName?start=user_12345When the user taps it, your bot receives /start user_12345, and you can map their Telegram chat ID to your internal user record. From that moment you are allowed to send. This is the same opt-in model most platforms use, and it is why a Telegram notification flow always begins with a link rather than a phone number.
5. Rate limits: the bot that works in testing and dies at launch
A bot that answered every message during development and started dropping replies the day it launched is usually hitting limits, not bugs. Telegram's published guidance for bots:
| Scope | Limit | What breaks first |
|---|---|---|
| Overall outbound | ~30 messages/second | Broadcasts and announcement sends |
| Per individual chat | ~1 message/second sustained | Multi-bubble replies sent in a burst |
| Per group | ~20 messages/minute | Community bots during busy periods |
When you exceed a limit the API returns 429 with a retry_after value in seconds:
{"ok":false,"error_code":429,"description":"Too Many Requests: retry after 34","parameters":{"retry_after":34}}Honour retry_after exactly. Retrying sooner extends the penalty, and sustained abuse can get a token limited for far longer than the number suggests. The fix for broadcasts is a token-bucket queue that paces sends below 30/second rather than a loop that fires as fast as the network allows.
A subtler version affects conversational bots: sending three quick bubbles to feel human - a greeting, then a question, then a menu - can trip the per-chat limit for that user. Insert a short delay between bubbles, which is also better conversation design, since three messages arriving in the same instant reads as a machine rather than a person.
6. Token problems, and the deploy that quietly used the wrong one
A malformed or revoked token returns:
{"ok":false,"error_code":401,"description":"Unauthorized"}Verify the token in isolation before suspecting anything else:
https://api.telegram.org/bot<TOKEN>/getMeA healthy token returns the bot's own username and ID. If getMe works from your laptop but the deployed bot returns 401, the token is fine and your environment variable is not - it is missing, truncated, or shadowed by another value.
Three failure modes account for most of these:
- A trailing newline. Tokens pasted into a secrets manager or a
.envfile often carry\n. Trim the value before use; the API rejects it and the error gives no hint about whitespace. - Quotes included.
TELEGRAM_TOKEN="123:ABC"in some shell and container setups produces a token that literally contains quote characters. - The staging token in production. If you built a second bot for testing, both tokens are valid and both pass
getMe. The bot responds - just not the one anyone is looking at. If users report silence while your logs show successful sends, check which bot the token belongs to.
Note also that regenerating a token via BotFather immediately invalidates the old one. If a bot stopped working the moment someone "rotated the key for security", that is your answer.
7. The bot sends nothing because the message itself is invalid
Some "not responding" reports are really "responding with an error you are not logging". The most common is a parse failure:
{"ok":false,"error_code":400,"description":"Bad Request: can't parse entities: Can't find end of the entity starting at byte offset 42"}This happens when you set parse_mode to MarkdownV2 or HTML and the message body contains characters the parser treats as markup. MarkdownV2 is particularly unforgiving: it requires escaping for _ * [ ] ( ) ~ ` > # + - = | { } . !, including inside ordinary prose. A perfectly innocent sentence ending in a full stop will fail.
This is the classic cause of a bot that handles most messages and dies on specific ones - a customer whose name contains an underscore, a product code with a hyphen, an address with brackets. It looks intermittent; it is entirely deterministic.
Three practical defences:
- Escape all interpolated user content before inserting it into a formatted message. Never trust that a name or a search term is markup-safe.
- Prefer
HTMLparse mode overMarkdownV2. It needs only three escapes -&,<,>- which is far easier to get right. - Log the full API response body, not just the status code. Telegram tells you the exact byte offset that failed, and that detail is lost if you only log "send failed".
The related error Bad Request: message is too long fires at 4,096 characters. AI-generated replies overrun this regularly, so split long responses on a paragraph boundary before sending rather than discovering the limit in production.
8. Chat not found, and other identifier mistakes
{"ok":false,"error_code":400,"description":"Bad Request: chat not found"}This means the chat_id you sent does not correspond to a chat your bot can reach. In order of likelihood:
- You used a username where an ID was needed. A bot can address a public channel as
@channelname, but private chats require the numeric ID. - The group was upgraded to a supergroup. This changes the chat ID, and the old one stops working permanently. Telegram sends a
migrate_to_chat_idfield in an update when it happens - handle it and store the new ID, or your bot silently loses the group. - Negative sign dropped. Group and channel IDs are negative, and supergroup IDs begin with
-100. Storing a chat ID in a column that strips the sign, or as an unsigned integer, produces exactly this error. - The bot was removed from the group and nobody updated the record.
A related trap: chat IDs can exceed 32 bits. Storing them in a 32-bit integer column truncates the value and produces "chat not found" for a subset of users - typically the newest ones, since IDs increase over time. Use a 64-bit integer or a string.
The 5-minute diagnostic, in order
Run these in sequence. Most bots are fixed before step 4.
| # | Check | Verdict if it fails |
|---|---|---|
| 1 | getMe returns your bot | Token is wrong, revoked, or has whitespace |
| 2 | getWebhookInfo url matches your intent | A stale webhook is stealing updates - deleteWebhook |
| 3 | last_error_message is empty | Endpoint is rejecting deliveries - read the message, it is accurate |
| 4 | pending_update_count is near zero | Nothing is draining the queue - no consumer running |
| 5 | Works in DM but not group | Privacy mode - disable it, then re-add the bot |
| 6 | Only one process holds the token | Two pollers - expect ~50% of messages to vanish |
| 7 | Full API error bodies are logged | You are hiding the answer from yourself |
That last row deserves emphasis. A large share of "my bot mysteriously stopped responding" reports resolve the moment someone logs the response body instead of the status code. Telegram's error descriptions are unusually specific - they name the byte offset, the seconds to wait, the method to call. Code that catches an exception and logs "send failed" throws away the fix.
Avoiding this class of problem entirely
Every cause above comes from operating the Bot API directly: you own the webhook lifecycle, the polling process, the retry semantics, the rate limiting, the escaping and the opt-in mapping. That is entirely reasonable for a hobby bot or a team with backend capacity to spare.
It is a poor trade when the bot is a support or sales channel for a business, because none of that work is differentiating. The webhook conflict, the parse-mode escaping and the token-bucket queue are the same problems for every bot on the platform.
A managed platform handles the transport layer so the failure modes in this guide never reach you. With Conferbot's Telegram integration, you connect a bot token and build the conversation in a visual flow editor - webhook registration, retries, rate limiting and message formatting are handled for you. The same flow deploys to WhatsApp, Discord, Slack and a website widget without rewriting it per platform, and message formatting adapts to each one's capabilities automatically.
Start free with Conferbot - the free plan includes 600 conversations a month and needs no credit card, so you can have the same flow running on a second channel this afternoon.
If you are debugging a Telegram bot today, work the diagnostic table above first - the answer is usually in getWebhookInfo. If you find yourself rebuilding the same transport plumbing for a third channel, that is the point where a platform starts paying for itself.
Was this article helpful?
Build and deploy in 10 minutes. No coding needed.
Telegram Bot Not Responding? Every Cause, Ranked by How Often It Happens FAQ
Everything you need to know about chatbots for telegram bot not responding? every cause, ranked by how often it happens.
About the Author
The Conferbot team writes about building, deploying, and improving AI chatbots.
View all articles