Telegram topic routing fixes for replies and cron
Cluster 24409. Data as of
Nothing open on GitHub. Fix merged in #28518 on 19 May 2026. Check it covers your case.
The short answer
- Known?
- Yes. 3 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #28518. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
unresolved. That is separate from the fix event. - Still open
- Nothing open on GitHub.
A bug report and several fixes addressing incorrect message routing in Telegram, covering DM-topic reply threading and cron delivery routing to dedicated topics via thread ID configuration.
Open issues0
Open PRs0
Fixes on record
-
#28518 fix(cron): route Telegram cron deliveries to dedicated topic via TELEGRAM_CRON_THREAD_ID (closes #24409)
files:
.env.example,cron/scheduler.py,tests/conftest.py,tests/cron/test_scheduler.py,website/docs/reference/environment-variables.md,website/docs/user-guide/features/cron.md,website/docs/user-guide/messaging/telegram.mdShow the diff
-
.env.example@@ -339,6 +339,7 @@ BROWSER_INACTIVITY_TIMEOUT=120 # TELEGRAM_ALLOWED_USERS= # Comma-separated user IDs # TELEGRAM_HOME_CHANNEL= # Default chat for cron delivery # TELEGRAM_HOME_CHANNEL_NAME= # Display name for home channel +# TELEGRAM_CRON_THREAD_ID= # Forum topic ID for cron deliveries; overrides TELEGRAM_HOME_CHANNEL_THREAD_ID for cron so replies work in topic mode # Webhook mode (optional — for cloud deployments like Fly.io/Railway) # Default is long polling. Setting TELEGRAM_WEBHOOK_URL switches to webhook mode. -
cron/scheduler.py@@ -292,10 +292,23 @@ def _get_home_target_chat_id(platform_name: str) -> str: def _get_home_target_thread_id(platform_name: str) -> Optional[str]: - """Return the optional thread/topic ID for a platform home target.""" + """Return the optional thread/topic ID for a platform home target. + + Telegram-only override: ``TELEGRAM_CRON_THREAD_ID`` takes precedence over + ``TELEGRAM_HOME_CHANNEL_THREAD_ID`` for cron delivery. When topic mode is + enabled, deliveries that land in the root DM (thread_id unset) end up in + the system-only lobby where the user cannot reply — the gateway returns + the lobby reminder and drops ``reply_to_message_id`` (#24409). Pointing + cron at a dedicated topic via this env var lets replies work as expected + without changing the lobby invariant. + """ env_var = _resolve_home_env_var(platform_name) if not env_var: return None + if platform_name.lower() == "telegram": + cron_thread = os.getenv("TELEGRAM_CRON_THREAD_ID", "").strip() + if cron_thread: + return cron_thread value = os.getenv(f"{env_var}_THREAD_ID", "").strip() if not value: legacy = _LEGACY_HOME_TARGET_ENV_VARS.get(env_var) -
tests/conftest.py@@ -243,6 +243,7 @@ def _looks_like_credential(name: str) -> bool: "TELEGRAM_HOME_CHANNEL", "TELEGRAM_HOME_CHANNEL_THREAD_ID", "TELEGRAM_HOME_CHANNEL_NAME", + "TELEGRAM_CRON_THREAD_ID", "DISCORD_HOME_CHANNEL", "DISCORD_HOME_CHANNEL_THREAD_ID", "DISCORD_HOME_CHANNEL_NAME", -
tests/cron/test_scheduler.py@@ -151,6 +151,53 @@ def test_bare_platform_delivery_preserves_home_thread_id(self, monkeypatch): "thread_id": "topic-7", } + def test_telegram_cron_thread_id_overrides_home_thread_id(self, monkeypatch): + """TELEGRAM_CRON_THREAD_ID wins over TELEGRAM_HOME_CHANNEL_THREAD_ID for cron (#24409).""" + monkeypatch.setenv("TELEGRAM_HOME_CHANNEL", "-1001234567890") + monkeypatch.setenv("TELEGRAM_HOME_CHANNEL_THREAD_ID", "5") + monkeypatch.setenv("TELEGRAM_CRON_THREAD_ID", "42") + + assert _resolve_delivery_target({"deliver": "telegram"}) == { + "platform": "telegram", + "chat_id": "-1001234567890", + "thread_id": "42", + } + + def test_telegram_cron_thread_id_sets_thread_when_home_thread_unset(self, monkeypatch): + """TELEGRAM_CRON_THREAD_ID supplies a thread when no home thread is configured.""" + monkeypatch.setenv("TELEGRAM_HOME_CHANNEL", "-1001234567890") + monkeypatch.delenv("TELEGRAM_HOME_CHANNEL_THREAD_ID", raising=False) + monkeypatch.setenv("TELEGRAM_CRON_THREAD_ID", "42") + + assert _resolve_delivery_target({"deliver": "telegram"}) == { + "platform": "telegram", + "chat_id": "-1001234567890", + "thread_id": "42", + } + + def test_telegram_cron_thread_id_does_not_leak_to_other_platforms(self, monkeypatch): + """TELEGRAM_CRON_THREAD_ID is Telegram-only; other platforms keep their own thread resolution.""" + monkeypatch.setenv("DISCORD_HOME_CHANNEL", "parent-42") + monkeypatch.setenv("DISCORD_HOME_CHANNEL_THREAD_ID", "topic-7") + monkeypatch.setenv("TELEGRAM_CRON_THREAD_ID", "42") + + assert _resolve_delivery_target({"deliver": "discord"}) == { + "platform": "discord", + "chat_id": "parent-42", + "thread_id": "topic-7", + } + + def test_explicit_telegram_topic_target_overrides_cron_thread_id(self, monkeypatch): + """Explicit ``telegram:chat:thread`` targets bypass TELEGRAM_CRON_THREAD_ID.""" + monkeypatch.setenv("TELEGRAM_CRON_THREAD_ID", "999") + + job = {"deliver": "telegram:-1003724596514:17"} + assert _resolve_delivery_target(job) == { + "platform": "telegram", + "chat_id": "-1003724596514", + "thread_id": "17", + } + def test_explicit_telegram_topic_target_with_thread_id(self): """deliver: 'telegram:chat_id:thread_id' parses correctly.""" job = { -
website/docs/reference/environment-variables.md@@ -248,6 +248,7 @@ For cloud sandbox backends, persistence is filesystem-oriented. `TERMINAL_LIFETI | `TELEGRAM_GROUP_ALLOWED_CHATS` | Comma-separated group/forum chat IDs; any member is authorized | | `TELEGRAM_HOME_CHANNEL` | Default Telegram chat/channel for cron delivery | | `TELEGRAM_HOME_CHANNEL_NAME` | Display name for the Telegram home channel | +| `TELEGRAM_CRON_THREAD_ID` | Forum topic ID to receive cron deliveries; overrides `TELEGRAM_HOME_CHANNEL_THREAD_ID` for cron only. Use in topic mode so replies to cron messages open a new session instead of hitting the system lobby (#24409). | | `TELEGRAM_WEBHOOK_URL` | Public HTTPS URL for webhook mode (enables webhook instead of polling) | | `TELEGRAM_WEBHOOK_PORT` | Local listen port for webhook server (default: `8443`) | | `TELEGRAM_WEBHOOK_SECRET` | Secret token Telegram echoes back in each update for verification. **Required whenever `TELEGRAM_WEBHOOK_URL` is set** — the gateway refuses to start without it (GHSA-3vpc-7q5r-276h). Generate with `openssl rand -hex 32`. | -
website/docs/user-guide/features/cron.md@@ -258,6 +258,17 @@ Semantics: `all` expands to every platform with a configured home channel. Zero `all` composes with explicit targets. `origin,all` delivers to the origin chat *plus* every other connected home channel, de-duplicating by `(platform, chat_id, thread_id)`. +### Telegram cron topic (`TELEGRAM_CRON_THREAD_ID`) + +When Telegram topic mode is enabled, the root DM is reserved as a system lobby — replies sent there are rebuffed with a lobby reminder and `reply_to_message_id` is dropped, so you cannot reply to a cron message that landed in the main chat. + +Point cron at a dedicated forum topic instead: + +1. In Telegram, open the bot DM and create a topic named e.g. `Cron`. Long-press the topic header → **Copy link**; the trailing integer is the topic's `message_thread_id`. +2. Set `TELEGRAM_CRON_THREAD_ID=<that id>` in your `.env`. + +This applies only to cron deliveries. `TELEGRAM_HOME_CHANNEL_THREAD_ID` (used elsewhere, e.g. restart notifications) is unchanged. Explicit `deliver="telegram:chat_id:thread_id"` targets continue to win over the env var. Replies to cron messages now arrive in the existing topic session, so you can act on them directly. + ### Response wrapping By default, delivered cron output is wrapped with a header and footer so the recipient knows it came from a scheduled task: -
website/docs/user-guide/messaging/telegram.md@@ -256,6 +256,16 @@ TELEGRAM_HOME_CHANNEL_NAME="My Notes" Group chat IDs are negative numbers (e.g., `-1001234567890`). Your personal DM chat ID is the same as your user ID. ::: +### Cron deliveries in topic mode + +If you have topic mode enabled in your bot DM, cron messages delivered to the root chat land in the system-only lobby — replying there opens no session and you see the "main chat is reserved for system commands" notice. Create a dedicated forum topic (e.g. `Cron`) and set: + +```bash +TELEGRAM_CRON_THREAD_ID=<topic_thread_id> +``` + +`TELEGRAM_CRON_THREAD_ID` overrides `TELEGRAM_HOME_CHANNEL_THREAD_ID` for cron deliveries only. Replies in that topic continue the topic's existing session. + ## Voice Messages ### Incoming Voice (Speech-to-Text)
-
1 PR closed without merging
From the close comments: merged via another change 1
-
#24798 fix(cron): route Telegram cron deliveries to a dedicated topic via TELEGRAM_CRON_THREAD_ID · merged via another change →
#28518
Closed issues · 1
- Closed#24409[Bug]: Replying to telegram topic main chat, not allowed.
How this cluster was grouped
-
Link evidence (
structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference. -
Pairwise link density
0.67: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Merged PRs
- #28518
- Closed issues
- 1
- Attempts
- 1 PR closed without merging
- Fix link checked
- 19 May 2026