The chatbot went silent: debugging failures that produce no error
Process up, logs clean, bot silent. Messenger bots fail without an exception. An external ping notices before a customer has to.
A loyal customer says the bot has been ignoring commands for half an hour. The panel is green, the process is up, CPU is idle, the log has no traceback. The developer’s first guess is the customer’s network. The customer is usually right. The expensive part is not the outage. It is that lack of visibility into agent context hid how long the bot had already been dead — and the missed starts were not sitting in a queue.
This is a reliability failure with no exception. The same pattern shows up on Telegram and on WhatsApp Business: the worker is alive, the messenger path is not.
Two different silences
When someone says “the bot is broken,” split the object:
- A site widget is a different network, a different origin, a different failure catalog.
- A messenger bot (Telegram, WhatsApp) is a long-lived process that either polls an API or waits for a webhook.
This note is the messenger case. We do not have our own measurements on widget TCP failures, and this lab does not retell other people’s guesses. Also set aside the generic list (“typo in the token,” “hosting unpaid”). The failures below are the ones that stay green in the process manager.
Process up, messenger unreachable
A bot is a loop that asks an external API whether anyone spoke. If api.telegram.org (or the WhatsApp Cloud endpoint) does not answer, the process waits. It does not have to crash. Sentry stays quiet. Grafana still shows running.
In June 2026 this lab measured that split on a production VPS. The symptom was sharp: start commands were ignored, outbound send timed out. Ordinary HTTPS and unrelated APIs were fine. Lookups of api.telegram.org via the host’s DNS landed on an address that never completed TCP. Inbound webhook deliveries from Telegram timed out the other way (Connection timed out). Everything except the messenger path was healthy.
A Mini App can still open from the customer’s phone while the chat is mute. That is expected. The Mini App hits your origin from the device. Telegram’s bot API is not on that path, so a broken API route does not show up in the WebView.
What to ask a contractor for, without needing to write the code:
- If inbound webhooks cannot reach the host, stop pretending they can. Switch to long polling so the server pulls updates.
- If DNS returns a dead address, pin a known-good Telegram DC IP and fail over across the DC subnet rather than waiting on a single A record. Neighbouring addresses in the same
/24can time out while one still works. - When you connect by IP, TLS still has to present
api.telegram.orgas SNI andHost. Otherwise the certificate check fails for a different reason and you debug the wrong layer. - Keep the process under a supervisor (
pm2or equivalent) so a crash is a bounce, not a silent stop. A bounce does not fix a dead API path; it only removes “the Node process exited” from the mystery.
The operator lesson is not “hardcode an IP and forget DNS.” It is: process liveness is not messenger reachability. Score the path you actually need.
Two consumers, one token
Telegram (and the same class of WhatsApp setups) allows one consumer of updates. Polling and webhook exclude each other. If a webhook was registered and you moved to polling without deleting it, new messages go somewhere you are not looking.
Worse: two live processes with the same token — an old pm2 instance you forgot to stop, or a laptop still running production credentials. They steal updates from each other. From the customer it looks random: mute, then a half-reply, then a dead keyboard. Rule: one token, one running instance. Staging gets its own bot. Production tokens do not belong in a developer’s local session.
That is the messenger version of a split brain. It is not “the model drifted.” It is two loops claiming the same inbox.
Lawful silence: the bot lost the right to speak
The third case is the one that feels unfair. The server is fine, the API is reachable, the code is correct, the channel is still quiet.
Rights move without a deploy. A channel owner cleans admins and clears can_post_messages. The bot is not failing. It is forbidden. This lab checks admin status and can_post_messages before every channel post — a hard probe, not an assumption from last month’s screenshot. The WhatsApp analogue is a WABA permission or a quality rating that stopped delivery while the process still “succeeds.”
Check the right at the moment of the action. Yesterday’s role is stale context.
A self-report is not evidence
The rule used on every bot this studio ships: the system’s report about itself is not proof that it works.
“Running,” “sent,” a green tile in the host panel — those are claims about intent. A fact is an independently observed effect: the message is in the customer’s chat, the file is on disk, the mail is in the inbox. Internal monitoring (“the process is up”) is exactly the signal that stayed green in the timeout incident above. The business was already losing leads.
If the thing that went quiet is not a button-bot but an LLM agent that was right yesterday and is confidently wrong today, that is a neighbouring disease — harness drift, not a mute TCP path. The control is still the same shape: do not let the loop grade its own health.
An external ping
Score the bot from outside. A small watcher, on a different host, behaves as an ordinary user: it sends a ping command every few minutes and waits.
If the production bot replies correctly within a few seconds, the path is alive. If not, the watcher pages on Telegram or WhatsApp (or a push you actually read). The difference between “a customer found a phone number after two days of silence” and “the on-call knew in minutes” is that one synthetic user.
Without it, silence lasts until the most loyal or most impatient person complains. Everyone else closes the thread. Those starts do not appear in the CRM in the morning. The process will still be green.
A chatbot that produces no error is not healthy. It is unobserved. Put the observer on the same path the customer uses.
- If a customer says the bot is mute, do you test the messenger path — or only that the process is running?
- Is there exactly one live consumer per bot token (no leftover webhook, no laptop sharing production)?
- Before a channel post, do you check can_post_messages (or the WhatsApp equivalent), not assume the role still holds?
- Does an independent script message the bot as a user and alert on Telegram or WhatsApp if the reply does not return?
- Would you know within minutes, rather than when someone finds a phone number to complain?