Telegram bot receives /start automatically: how it triggered my kill switch (one-line fix)
I opened my Telegram bot's chat, tapped Start, typed nothing else, and my bot executed "release" on its emergency stop. I found that Telegram sends /start to the bot when you first open its chat and tap Start. One of the words I had mapped to my "release" command was from the start family, so that automatic /start was read as "release." I fixed it in one line by removing every start-family word from commands that change state and keeping /start for a usage message only.
Contents
The setup: a 3-second emergency stop on my phone
I hand money-related chores to a few AI assistants. One assigns work, one processes it, and one checks the results. With the system running on its own, I worried about whether I could stop it from my phone in three seconds if something went wrong. Parts of it run in the cloud, so it keeps going even when my home PC is off.
So the first thing I built was a remote emergency stop. I send "stop" to a Telegram bot and running work halts. I send "release" and it resumes. I created a bot, registered a few commands for stop and release, and wired them to halt or resume the system. That took about half a day. (My real commands are Korean words. I translate them as "stop" and "release" in this post.)
The symptom: "release executed" in the log
During a live test, I triggered the stop and watched everything halt.
Then I read the log and found a line I didn't expect: release executed (translated from Korean). I had never sent "release." I had put the brakes on, done nothing, and the brakes had come off by themselves.
I checked the timestamp. It matched the exact moment I first opened that bot's chat and tapped Start.
The cause: Telegram's automatic /start
This part is normal Telegram behavior. When someone opens a chat with a bot for the first time and taps the Start button, Telegram sends /start to the bot. To a person, it's a greeting button. To the bot, it's a command like any other.
My mistake was in my own command list. Among the words I had mapped to "release," I had included a start-family word. So the sequence was:
- Stop is active.
- I open the bot chat for the first time and tap the Start button.
- Telegram sends
/starton my behalf. I never typed it. - My bot matches it to "release" and resumes the system.
Opening the chat and tapping Start had released the emergency stop.
The bot handles a typed command the same way as one the app sends automatically. Both arrive as "a start command came in." If you connect a state-changing action to /start, it can run when no one meant it to.
Reproduce it in 5 minutes
You can test any bot for this. The source of the risk is the mapping, so here is the shape of the bug in simplified form. This is an illustration, not my actual code. Every command handler should also check that the message comes from your own allowed Telegram user ID before doing anything.
# Shape of the bug (illustration)
COMMANDS = {
"stop": halt_system,
"release": resume_system,
"start": resume_system, # <- this is the hole
}
# After the fix (illustration)
COMMANDS = {
"stop": halt_system,
"release": resume_system,
"start": show_usage, # /start only explains how to use the bot
}
To test a bot safely:
- Point the state-changing commands at a test handler that only writes a log line, such as "release would run here." Don't test against the real stop switch or anything that moves money.
- Make sure the handler checks the sender against your allowed user ID, and test from an account on that list.
- Open the bot from a new chat so you get the first-visit Start button. Tap Start and type nothing else.
- Check the log. If anything other than a usage message ran, that action is tied to
/start.
The test takes 5 minutes. I skipped it at first and spent half a day finding the cause.
The one-line fix
I removed the start-family word from my release command list. (Half a day to figure that out.)
/startnow only replies with "here's how this bot works."- Any command that changes state needs a distinct word I have to type on purpose.
I re-ran the first-visit test: opened a fresh chat, tapped Start, typed nothing. Only the usage message showed, and nothing changed.
I also wrote this rule into the instructions of the AI assistant that checks results: never design bot commands this way again. (I had built it, and I still got caught.)
One more check I now do. If you manage your command list in BotFather, compare the names you registered there with the names your code actually reacts to, one by one. I skipped that 5-minute comparison because it felt tedious. The list you see and the words your code responds to can differ in small ways, and /start slips through that gap.
3 kinds of commands that should never share a word with /start
After this, I wrote down 3 risky combinations:
- Commands that lower safety, such as releasing an emergency stop.
- Commands that move money, such as automated buys or transfers.
- Commands that are hard to undo, such as delete or reset.
If "start transfer" were mapped to start, opening a fresh bot chat and tapping Start could trigger a transfer. My mistake hit a stop switch, not money. If it had been a transfer, the cost would have been real.
Checklist: safe bot command design
| Check | Warning sign | Safer design |
|---|---|---|
| What /start does | /start is wired to an action that changes state | Reserve /start for a usage message |
| Command names | Stop-release, transfer or delete commands use common words like start, go, on | Use distinct words that won't collide by accident |
| Automatic vs typed | Typed commands and automatic ones are handled the same way | Add a confirm step for anything hard to undo |
| First-visit test | You registered commands but never opened a fresh chat | Open a new chat, tap Start, and confirm nothing else runs |
If any row shows a warning sign, open your bot's command list today. I only tried the last check after this happened to me. It caught in 5 minutes what cost me half a day. I now run it first on every new bot.
What I took from it
Building the remote stop took half a day. Making sure it couldn't be released by accident took several more days after that. For automation that touches money, I put this ahead of returns. After this, I wanted to know how to stop the system if the wrong command ran.
FAQ
Why does Telegram send /start automatically?
It's normal behavior. When a user enters a bot chat for the first time and presses Start, Telegram sends /start to the bot. Instead of trying to block it, change what it's connected to: keep risky actions away from /start.
Should I remove /start entirely?
No. It's useful as a usage guide. The risk is when state-changing commands (release, transfer, delete) share a start-family word.
How do I check in advance whether tapping Start runs something?
Open the bot in a new chat, tap Start, and type nothing else. Point risky commands at a test handler first. If it does anything besides showing usage, that action is tied to /start.
Do Discord or Slack bots have the same problem?
Not in exactly the same way, but the principle carries over. If a bot can't tell a typed command from an automatic signal, an action can fire at a moment nobody intended.
Comments
Post a Comment