> ## Content Index
> Fetch the complete content index at: https://blog.melardenio.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Telegram Bot 409 Conflict: Terminated by setWebhook Request
- URL: https://blog.melardenio.com/telegram-bot-409-conflict-terminated-by-setwebhook-request/
- Published: 2026-10-13T14:15:39.000Z
- Updated: 2026-10-13T14:33:05.000Z
- Author: Rommell Ardenio
- Tags: Software & Homelab

My trip-ticket Telegram bot went quiet for three days, and I didn't notice until I went looking for it. The service was crashing with one line in its log: **409: Conflict: terminated by setWebhook request**. If you are searching for that error, the short answer is that two things are trying to receive updates for the same bot token, and Telegram allows only one of them.

## What I had running

The bot is *@triptickettbot*, the notification bot for Project ATLAS, my trip-ticket app. It runs as a standalone service on my homelab (`atlas-bot.service`) and uses long polling through Telegraf. It asks Telegram for new updates in a loop.

Separately, I have an n8n workflow, "INVOICE ENCODING WF", that starts from a Telegram Trigger node. That node works the other way around. It registers a webhook with Telegram and waits for Telegram to call it.

The problem is that I had given both the same bot token.

## Why that breaks

For any given bot token, Telegram delivers updates in exactly one of two ways: a registered webhook, or long polling with `getUpdates`. They are mutually exclusive. When the n8n workflow registered its webhook, Telegram switched the token to webhook delivery and terminated the polling connection the ATLAS service was using. The service got the 409 and crashed. The webhook registration kept knocking it over each time it tried again, until I stopped the service by hand. It then stayed stopped for three days.

From n8n's side nothing looked wrong. The Telegram Trigger was working. It was just working for the wrong bot.

## How to check for it

If a Telegram bot of yours stops responding and its logs show a 409, check what the token is currently set up for. Telegram's `getWebhookInfo` method tells you. A webhook URL that points somewhere you don't expect, like an unrelated n8n workflow, means another consumer has claimed the token. Any polling service using the same token will be starved of updates.

## The fix is a second token, not a restart

Restarting the crashing service doesn't help. It will start polling, the webhook will knock it over again, and you are back in the loop. The real fix is to give each bot its own token. In Telegram's BotFather, create a separate bot for the second consumer, put its token in its own n8n credential, and leave the original bot to the service that was using it first.

## What I changed in how I work

I now treat a Telegram bot token like a database password: one token, one consumer. Every bot, whether it is an n8n workflow or a standalone service, gets its own bot and its own credential. It costs a couple of minutes in BotFather, and it would have saved me three days of a silently dead bot.

The part that bothers me most is the silence. A bot that stays quiet for three days looks the same as a bot with nothing to say, so a standalone service needs its own check that it is actually running.

## What the crash loop looked like

A systemd restart policy hides this kind of failure well. On the homelab today, `atlas-bot.service` is set to `Restart=on-failure` with a five-second delay (`RestartUSec=5s`). I read that from `systemctl show atlas-bot.service -p Restart -p RestartUSec`, so it describes the unit as configured now, not a record of the exact days it failed. With a policy like that, a bot that dies on a 409 is started again five seconds later, hits the same 409, and dies again. It never stays up long enough to answer a message, and it keeps getting restarted until you stop it by hand.

## Why n8n made it silent

A Telegram Trigger node registers its webhook with Telegram when you activate the workflow. After that it does nothing visible until a message arrives. Most of the time none does, so the workflow looks healthy, with an "active" badge, while it quietly holds the token. Nothing in n8n tells you that a separate program is trying to poll the same bot.

## The two Telegram calls worth knowing

Both are plain HTTPS calls to the Bot API. Replace `<TOKEN>` with your bot's token, and keep the token out of screenshots, logs and shell history you share.

```
curl -s "https://api.telegram.org/bot<TOKEN>/getWebhookInfo"
curl -s "https://api.telegram.org/bot<TOKEN>/deleteWebhook"
```

`getWebhookInfo` returns the registered URL, the number of pending updates and the last delivery error Telegram saw. An empty `url` means no webhook is set, so polling will work. A URL you don't recognise means another consumer owns the token. `deleteWebhook` removes the webhook so polling works again, but only until the other program registers it again. That makes it a diagnosis step, not a fix.

## A checklist for when a bot goes quiet

1. Read the service log. If it says 409 Conflict, stop looking at your code: the code is not the problem.
2. Call `getWebhookInfo` and read the `url` field.
3. Search your automation tools and services for the same token. Telegram Trigger nodes in n8n are the usual suspect.
4. Create a separate bot in BotFather for one of the two consumers and update only that consumer's credential.
5. Restart the polling service once and confirm it stays up.

## Catching it earlier next time

The cheapest check is to ask systemd how many times the service has restarted. `systemctl show atlas-bot.service -p NRestarts` prints a counter, and a number that keeps climbing means a crash loop. The service can briefly report as active between crashes, so "is it running?" is the wrong question. "Has it restarted since yesterday?" is the right one, and it belongs in any health check you run.

It is also worth saying what I would not do again: share a token "just for now". The shortcut saved a minute of setup and cost three days of a dead bot.

## Related reading

- [Teaching a Telegram Bot to File Trip Tickets](https://blog.melardenio.com/teaching-a-telegram-bot-to-file-trip-tickets/)
- [One SSH Key, One Job: Swapping Ollama for the Claude CLI](https://blog.melardenio.com/one-ssh-key-one-job-swapping-ollama-for-the-claude-cli/)
- [Cloudflare Tunnel 502 Bad Gateway: Removing a Dead Route](https://blog.melardenio.com/cloudflare-tunnel-502-bad-gateway-removing-a-dead-route-and-what-access-hides-from-scanners/)