Debugging Telegram Webhook Failures: Start With getWebhookInfo
Debugging Telegram Webhook Failures: Start With getWebhookInfo
Your Telegram webhook is not working: the bot has stopped receiving updates. The process is running, the logs are empty, and nothing is obviously wrong.
Before you check anything else, make one API call.
Step 1: ask Telegram what it sees
https://api.telegram.org/bot<TOKEN>/getWebhookInfo
This returns Telegram's own view of your webhook: the registered URL, the number of pending updates it is holding, and — the field that solves most cases — the last error it encountered trying to deliver to you, if there has been one — last_error_message is Optional on WebhookInfo and is simply absent when Telegram has never failed to deliver.
That is Telegram telling you, in plain text, why it cannot reach your bot. It is one API call, it needs no access to your server, and it is the only place Telegram's side of the failure is visible.
Read three things:
The URL. Is it the one you expect? A deploy that reset it, a stale registration from a previous environment, or an empty string means updates are going nowhere or somewhere else.
Pending update count. Growing means Telegram is queuing updates it cannot deliver — but that queue is capped: updates are "stored on the server until the bot receives them either way, but they will not be kept longer than 24 hours". Anything older is gone whether or not you drop the backlog, so a multi-day outage is a data-loss event, not just a delay. Zero with no traffic means either genuinely no activity, or updates are being delivered somewhere else.
Last error. Usually names the problem outright — TLS failure, connection refused, timeout, wrong status code. Read last_error_date alongside it: last_error_message is "the most recent error", not a live status, and nothing says it clears once delivery recovers. An error timestamped days ago with a pending count of zero is history; one from the last few minutes is your bug. last_synchronization_error_date is Telegram-side and not about your server.
Step 2: check the four things that break most often
The certificate
Webhooks require HTTPS on Telegram's servers. An expired certificate stops delivery completely and produces no error on your side, because the request never reaches your server.
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
| openssl x509 -noout -enddate
Check what is actually being served, not what is on disk — a renewed certificate that was never loaded is a common and confusing case. See the certificate requirement.
The port
Telegram supports exactly four: 443, 80, 88, 8443 — "Ports currently supported for webhooks: 443, 80, 88, 8443." If your platform assigned port 3000 and you registered that URL against api.telegram.org, it will not work. A self-hosted local Bot API server can "Use any port for the webhook", but that is not what you are running unless you set it up deliberately.
The status code
Telegram expects 2XX. From the documentation: "In case of an unsuccessful request (a request with response HTTP status code different from 2XY), we will repeat the request and give up after a reasonable amount of attempts."
Anything else — including a 301 redirect your proxy adds — counts as failure. Return 200 immediately, then do the work asynchronously. A handler that processes before responding will eventually time out under load.
Something else consumed the update
Two instances behind the same URL will split deliveries between them. A leftover getUpdates poller cannot steal updates — the docs are explicit that getUpdates "will not work if an outgoing webhook is set up", and such a poller just fails with 409 — but a second process that calls setWebhook or deleteWebhook will repoint or clear your registration, which shows up as a changed or empty URL in step 1. Symptoms are intermittent missing updates rather than total silence.
Step 3: prove the path yourself
If getWebhookInfo is clean, test the chain by hand.
Can you reach your own endpoint?
curl -i -X POST https://yourdomain.com/your-webhook-path \
-H 'Content-Type: application/json' \
-d '{"update_id":1}'
Expect a fast 2XX. A redirect, a 404 from a proxy rule, or a slow response are all findings.
Re-register and clear the backlog.
curl "https://api.telegram.org/bot<TOKEN>/setWebhook?url=https://yourdomain.com/path&drop_pending_updates=true"
drop_pending_updates discards the queue — useful when a backlog of stale updates would otherwise flood you on recovery. Only use it when you are willing to lose what is still there, which is less than you think: nothing older than 24 hours is in that queue anyway.
Then check getWebhookInfo again.
Parameters worth setting
Several setWebhook options prevent whole classes of problem:
secret_token — Telegram includes it as an X-Telegram-Bot-Api-Secret-Token header so you can verify requests actually came from Telegram. Without it, your webhook URL is an open endpoint anyone who learns it can POST to. Set this — "1-256 characters. Only characters A-Z, a-z, 0-9, _ and - are allowed." Base64 output containing = or + will be rejected by setWebhook; use hex or a URL-safe alphabet.
allowed_updates — receive only the types you handle. Less traffic, less parsing. It is also an opt-in, not only a filter: the default is "all update types except chat_member, message_reaction, and message_reaction_count", so those three arrive only if you name them. And a list set once will silently exclude any type Telegram adds later — the opposite of "fewer surprises". If one update type never arrives while everything else does, read allowed_updates back out of getWebhookInfo before looking anywhere else.
max_connections — 1 to 100, default 40. Lower it if your server is small; a bot being overwhelmed by concurrent deliveries looks like random 502s.
ip_address — pins delivery to a fixed IP instead of resolving DNS. Useful if DNS is a suspect.
When it is not the webhook
If getWebhookInfo is clean and your endpoint responds correctly, the problem is downstream:
Token revoked — regenerated in @BotFather. Every API call fails immediately and loudly.
Bot removed from the chat — updates for that chat stop, everything else continues.
Users blocked the bot — sends to those users fail until they unblock. Not an outage; it looks like declining engagement. Telegram tells you both ways: my_chat_member fires on block and on unblock in private chats, and it is in the default update set, so track status from that update rather than treating a failed send as final.
Your handler is throwing — you return 200, Telegram is satisfied, and the work silently fails. Check your own error logs, not Telegram's.
That last one is worth calling out: returning 200 and then crashing looks perfectly healthy from Telegram's side. If getWebhookInfo is clean and updates are arriving, the bug is yours.
The diagnostic order
getWebhookInfo— read URL, pending count, last error with its timestamp, andallowed_updates- Certificate expiry, checked against the live endpoint
- Port is 443, 80, 88 or 8443
- Your endpoint returns a fast 2XX to a manual POST
- No second instance behind the URL, and nothing else calling
setWebhook/deleteWebhook - Re-register with
drop_pending_updatesif the backlog is stale - If all clean — the failure is in your handler
Most outages resolve at step 1 or step 2. Both take under a minute, and together they cover the large majority of "my bot stopped working" cases.
Behaviour and parameters quoted from Telegram's Bot API documentation, verified September 2026.