Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions gateway/platforms/telegram.py
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,8 @@ async def send(
except ImportError:
_NetErr = OSError # type: ignore[misc,assignment]

effective_thread_id = int(thread_id) if thread_id else None

for i, chunk in enumerate(chunks):
should_thread = self._should_thread_reply(reply_to, i)
reply_to_id = int(reply_to) if should_thread else None
Expand All @@ -681,9 +683,16 @@ async def send(
text=chunk,
parse_mode=ParseMode.MARKDOWN_V2,
reply_to_message_id=reply_to_id,
message_thread_id=int(thread_id) if thread_id else None,
message_thread_id=effective_thread_id,
)
except Exception as md_error:
# Thread not found β€” drop thread_id and retry immediately
if "thread not found" in str(md_error).lower():
if effective_thread_id is not None:
logger.warning("[%s] Thread %s not found, retrying without thread_id", self.name, effective_thread_id)
effective_thread_id = None
continue
raise
# Markdown parsing failed, try plain text
if "parse" in str(md_error).lower() or "markdown" in str(md_error).lower():
logger.warning("[%s] MarkdownV2 parse failed, falling back to plain text: %s", self.name, md_error)
Expand All @@ -693,12 +702,18 @@ async def send(
text=plain_chunk,
parse_mode=None,
reply_to_message_id=reply_to_id,
message_thread_id=int(thread_id) if thread_id else None,
message_thread_id=effective_thread_id,
)
else:
raise
break # success
except _NetErr as send_err:
# Thread not found may also surface as a network-layer error
if "thread not found" in str(send_err).lower():
if effective_thread_id is not None:
logger.warning("[%s] Thread %s not found (network layer), retrying without thread_id", self.name, effective_thread_id)
effective_thread_id = None
continue
if _send_attempt < 2:
wait = 2 ** _send_attempt
logger.warning("[%s] Network error on send (attempt %d/3), retrying in %ds: %s",
Expand Down Expand Up @@ -1811,6 +1826,12 @@ def _build_message_event(self, message: Message, msg_type: MessageType) -> Messa
if not chat_topic:
chat_topic = created_name

# If no DM topic was resolved, Telegram may have set message_thread_id
# to indicate a reply chain (not a real forum topic). Drop it so
# downstream send calls don't fail with "Message thread not found".
if not chat_topic:
thread_id_str = None

# Build source
source = self.build_source(
chat_id=str(chat.id),
Expand Down
9 changes: 8 additions & 1 deletion gateway/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -4865,7 +4865,14 @@ def progress_callback(tool_name: str, preview: str = None, args: dict = None):
# final reply will be threaded under the original message via reply_to.
# Use event_message_id as fallback so progress messages land in the
# same thread as the final response instead of going to the DM root.
_progress_thread_id = source.thread_id or event_message_id
# NOTE: Only use event_message_id fallback for platforms that support
# threading by message ID (Slack, Discord). Telegram uses dedicated
# forum topic IDs for message_thread_id β€” passing a regular message ID
# causes "Message thread not found" errors.
if source.platform in ("slack", "discord"):
_progress_thread_id = source.thread_id or event_message_id
else:
_progress_thread_id = source.thread_id
_progress_metadata = {"thread_id": _progress_thread_id} if _progress_thread_id else None

async def send_progress_messages():
Expand Down
Loading