Telegram returns "terminated by other getUpdates request" when another process is already polling with the same bot token. The fix is operational, not a retry loop: stop every copy of the poller, verify that no webhook is active, and start one instance.
Stop the conflict in five minutes
- 01Stop the current deploymentPause the service that is printing the 409 error. This quiets the logs and prevents it from fighting the instance you are trying to find.
- 02Close local copiesStop terminals, editor previews, notebooks, Docker containers, and process managers that use the same token.
- 03Check the old hostA migration often leaves the previous service running. Disable it, including any restart policy that could bring it back.
- 04Set the replica count to oneLong polling is a single-consumer path for one token. A rolling deploy or two replicas can overlap and trigger the same error.
- 05Start one instance and testWatch its startup log, send one message, and confirm the 409 line does not return.
Where the second poller usually hides
| WHEN THE ERROR BEGAN | MOST LIKELY DUPLICATE | WHAT TO INSPECT |
|---|---|---|
| Right after deployment | The local development process | Terminal, editor run panel, Docker Desktop |
| During a migration | The old hosting service | Previous provider dashboard and auto-restart setting |
| After enabling scaling | A second replica | Replica count, cluster mode, process manager |
| Only during releases | Rolling deployment overlap | Release strategy and shutdown handling |
| After switching from webhook to polling | A webhook is still configured | Telegram getWebhookInfo result |
Do not assume one dashboard service means one process. PM2 cluster mode, Gunicorn workers, Kubernetes replicas, and a parent process that starts the bot twice can all create multiple pollers inside one deployment.
Rule out a webhook mismatch
Telegram supports long polling and webhooks, but not both at the same time. Call getWebhookInfo through the Bot API. If the returned url is not empty and you intend to poll, call deleteWebhook first. Use drop_pending_updates only when discarding queued updates is genuinely acceptable.
Why catching the exception does not fix it
A handler that catches 409 and immediately retries only makes two processes interrupt each other faster. Backoff makes the logs quieter, but the ownership problem remains. One process must stop polling.
Token rotation is also unnecessary when both processes belong to you. Rotate only when you cannot identify the other poller or suspect the token escaped. A new token invalidates every deployment, so update the intended host after rotating it.
Prevent it on the next deploy
- Run one polling replica per bot token.
- Stop the old host before starting the new poller during a migration.
- Handle SIGTERM so the polling loop closes promptly during a release.
- Keep development and production bots on separate tokens.
- Name the bot identity in startup logs without printing the token.
- Use a webhook architecture when you intentionally need multiple HTTP workers.
On Spocket, deploy updated code to the same app name instead of creating a second app with the same token. Check /documentation/logs-and-status for the active process and recent output. If you are moving a poller here, follow the one-instance cutover on /run/telegram.