Your bot works in a private chat and goes silent in the group. The chat ID looked wrong, so you cleaned it up: dropped the minus sign, or trimmed the -100 that seemed like a typo. Telegram now answers 400 Bad Request: chat not found, and nothing in that reply tells you the characters you removed were carrying information.
The minus sign and the -100 are arithmetic. Telegram's four ID sequences (users, basic groups, supergroups and channels, secret chats) overlap in the underlying MTProto API, so the Bot API packs them into one signed range where the sign and prefix tell you which type you are holding. Below are the exact ranges from the official spec and the three bugs they cause.
Table of contents
- The four ranges
- Why the offset exists
- Column types for chat IDs
- Basic groups that become supergroups
- The 409 conflict on getUpdates
- Reading the ID out of getUpdates
- Username or numeric ID
- FAQ
- Sources
The four ranges
A Bot API dialog ID is a single 64-bit sequence built from the four MTProto sequences. Telegram documents the bounds precisely: the whole space runs from -4000000000000 to 1099511627775.
Users are positive, from 1 to 0xffffffffff. An MTProto user ID and a Bot API user ID are the same number, with no conversion.
Basic groups are negated: botApiChatId = -chatId, where the MTProto side runs 1 to 999999999999. A basic group therefore lands between -999999999999 and -1.
Supergroups and channels share one MTProto sequence running 1 to 997852516352. The conversion adds a trillion, then negates: botApiChannelId = -(1000000000000 + channelId). That gives -1997852516352 to -1000000000001, and it is where the -100 on screen comes from. It is the leading digits of 1000000000000, not a prefix string.
Secret chats sit outside the bot's world. Their MTProto IDs run -2147483648 to 2147483647, and bots never see them.
A channel ID and a supergroup ID are indistinguishable by range, because they are the same sequence. If your code needs to know which one it has, read chat.type from the update, which is private, group, supergroup or channel.
Why the offset exists
Without it, ID 4823 would be ambiguous. It could be user 4823, basic group 4823, or channel 4823, and all three exist independently in MTProto. The Bot API needed one flat namespace, so it used the sign for "not a user" and a trillion-offset for "not a basic group."
A range check alone then tells you the peer type before you have parsed anything else. That is why the docs recommend converting MTProto IDs to Bot API form even when you keep separate tables per peer type.
Column types for chat IDs
Telegram's wording on the Chat.id field is a warning, not a footnote: the number may have more than 32 significant bits and "some programming languages may have difficulty/silent defects in interpreting it." It caps the damage by promising at most 52 significant bits, so a signed 64-bit integer or a double-precision float both hold it exactly.
52 is the mantissa of an IEEE-754 double, which is what a JSON number becomes in JavaScript. Telegram sized the ID space to survive JSON.parse.
A 32-bit integer column still breaks it. -1001234567890 does not fit in INT, and a database or language that silently truncates hands you a plausible looking wrong number. Store chat IDs as BIGINT, int64, Python int, or a string you never do arithmetic on.
Basic groups that become supergroups
This bug breaks working code without a deploy. Add a member past the limit, enable public history, or attach a discussion group to a channel, and Telegram migrates the basic group to a supergroup. The chat ID changes range mid-life, from the -999999999999 band into the -1000000000001 band.
Telegram does tell you. The migration arrives as a service message carrying migrate_to_chat_id on the old chat, and the new supergroup's first message carries migrate_from_chat_id. Both fields have the same 52-bit caveat as Chat.id.
Most bots ignore both fields, keep sending to the stored basic group ID, and start collecting chat not found from a group that visibly still exists. If your bot persists chat IDs, handle migrate_to_chat_id by updating the stored row. It takes about a dozen lines and prevents a silent outage months later.
The 409 conflict on getUpdates
Before you can read a chat ID out of getUpdates, the bot has to be allowed to poll. Telegram's note on the method is one line: "This method will not work if an outgoing webhook is set up." A bot that has ever run in production almost certainly has one, and you get 409 Conflict instead of your updates.
deleteWebhook fixes it. It switches the bot back to polling and returns True. It accepts drop_pending_updates, and passing True discards the queue you were trying to read, so leave it off for an ID lookup. If that bot's server is live, it will re-register its webhook on the next call, so point it back when you are done.
Undelivered updates are held server-side for a maximum of 24 hours. If the group message you want is from last week, it is gone, so send a fresh one.
The Telegram Chat ID Finder runs this sequence in the browser. It validates the token, names the bot so you know which one you are holding, detects the 409 and offers the deleteWebhook call, then reports the ID with its sign and offset intact. The token goes from your device to api.telegram.org and nowhere else.
Reading the ID out of getUpdates
To do it by hand, this is the call:
curl -s "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates" \
| python3 -c 'import json,sys
for u in json.load(sys.stdin)["result"]:
m = u.get("message") or u.get("channel_post") or {}
c = m.get("chat", {})
print(c.get("type"), c.get("id"), c.get("title") or c.get("username"))'Three details decide whether you get anything back. Group messages arrive under message, but channel posts arrive under channel_post, so a channel lookup that only reads message finds nothing. In a group with privacy mode on, the bot only sees commands addressed to it, so send /start@your_bot rather than "hello". And for a channel, the bot must be an admin before any post reaches it.
An empty result with no error means the bot is fine and the queue is empty. Post again and re-run.
Username or numeric ID
For a public channel or supergroup you can skip the numeric ID and pass @channelusername as chat_id. It reads better in config files, but usernames can be changed or released by their owner, and then your bot is posting to whoever claimed it next.
Numeric IDs never change for the lifetime of a chat, apart from the migration case above. For anything private, or anything you care about, store the number and keep the username as a label for humans reading the config.
If your bot posts computed numbers, the JSON Formatter is handy for reading a getUpdates payload before you write the parser.
FAQ
What does the -100 in a Telegram chat ID mean?
It is the leading digits of the trillion offset the Bot API adds to supergroup and channel IDs before negating them: botApiChannelId = -(1000000000000 + channelId). Remove it and you get an ID in the basic-group range that does not exist, so Telegram replies chat not found.
Can a Telegram chat ID change?
Yes, in one case. When a basic group is migrated to a supergroup, the ID moves from the -999999999999 band to the -1000000000001 band. Telegram sends migrate_to_chat_id on the old chat and migrate_from_chat_id on the new one.
How do I tell a channel ID from a supergroup ID?
Not by the number, since they share one MTProto sequence and one Bot API range. Read chat.type from the update, which returns supergroup or channel.
Why does getUpdates return 409 Conflict?
The bot has a webhook registered, and Telegram allows one delivery method at a time. Call deleteWebhook to switch to polling, without drop_pending_updates if you still want the queued messages.
Sources
- Bot API dialog IDs, core.telegram.org: the four sequences, conversion formulas, and exact ranges, read 11 September 2026
- Telegram Bot API, core.telegram.org:
Chat.id52-bit note,chat.typevalues,migrate_to_chat_id,getUpdateswebhook restriction,deleteWebhook, and the 24-hour update retention, Bot API 10.3 of 24 August 2026, read 11 September 2026 - Channels, supergroups, gigagroups and basic groups, core.telegram.org: supergroup migration and the 200,000 member limit, read 11 September 2026
