From 1ae120ef28c1bf41b86cd7ebb586e86e6d698354 Mon Sep 17 00:00:00 2001 From: Yufeng He <40085740+he-yufeng@users.noreply.github.com> Date: Tue, 14 Apr 2026 17:32:22 +0800 Subject: [PATCH] fix: prevent Telegram media group exceptions from being silently swallowed process_media_group() is invoked by APScheduler via add_job(). If convert_message() or handle_msg() raises (e.g. get_file() network timeout, file download failure), APScheduler catches the exception internally and only logs it through its own logger, which is often not configured in AstrBot. The result is that the media group silently disappears with no trace in the application logs. Two changes: - Wrap the body of process_media_group() in try/except so failures are logged through AstrBot's own logger with full traceback. - Register an EVENT_JOB_ERROR listener on the scheduler as a safety net, so any future scheduled job that throws will also surface in the logs. Fixes #7512 --- .../platform/sources/telegram/tg_adapter.py | 54 +++++++++++-------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/astrbot/core/platform/sources/telegram/tg_adapter.py b/astrbot/core/platform/sources/telegram/tg_adapter.py index a37e5cfd41..3a921f25f0 100644 --- a/astrbot/core/platform/sources/telegram/tg_adapter.py +++ b/astrbot/core/platform/sources/telegram/tg_adapter.py @@ -6,6 +6,7 @@ from contextlib import suppress from typing import cast +from apscheduler.events import EVENT_JOB_ERROR from apscheduler.schedulers.asyncio import AsyncIOScheduler from telegram import BotCommand, Update from telegram.constants import ChatType @@ -80,6 +81,12 @@ def __init__( self.last_command_hash = None self.scheduler = AsyncIOScheduler() + self.scheduler.add_listener( + lambda ev: logger.error( + "Scheduled job %s raised: %s", ev.job_id, ev.exception, exc_info=ev.exception + ), + EVENT_JOB_ERROR, + ) self._terminating = False self._loop: asyncio.AbstractEventLoop | None = None self._polling_recovery_requested = asyncio.Event() @@ -695,31 +702,36 @@ async def process_media_group(self, media_group_id: str) -> None: f"Processing media group {media_group_id}, total {len(updates_and_contexts)} items" ) - # Use the first update to create the base message (with reply, caption, etc.) - first_update, first_context = updates_and_contexts[0] - abm = await self.convert_message(first_update, first_context) + try: + # Use the first update to create the base message (with reply, caption, etc.) + first_update, first_context = updates_and_contexts[0] + abm = await self.convert_message(first_update, first_context) - if not abm: - logger.warning( - f"Failed to convert the first message of media group {media_group_id}" - ) - return + if not abm: + logger.warning( + f"Failed to convert the first message of media group {media_group_id}" + ) + return - # Add additional media from remaining updates by reusing convert_message - for update, context in updates_and_contexts[1:]: - # Convert the message but skip reply chains (get_reply=False) - extra = await self.convert_message(update, context, get_reply=False) - if not extra: - continue + # Add additional media from remaining updates by reusing convert_message + for update, context in updates_and_contexts[1:]: + # Convert the message but skip reply chains (get_reply=False) + extra = await self.convert_message(update, context, get_reply=False) + if not extra: + continue - # Merge only the message components (keep base session/meta from first) - abm.message.extend(extra.message) - logger.debug( - f"Added {len(extra.message)} components to media group {media_group_id}" - ) + # Merge only the message components (keep base session/meta from first) + abm.message.extend(extra.message) + logger.debug( + f"Added {len(extra.message)} components to media group {media_group_id}" + ) - # Process the merged message - await self.handle_msg(abm) + # Process the merged message + await self.handle_msg(abm) + except Exception: + logger.error( + f"Failed to process media group {media_group_id}", exc_info=True + ) async def handle_msg(self, message: AstrBotMessage) -> None: message_event = TelegramPlatformEvent(