fix(webhook): avoid MissingGreenlet on expired ORM instance after commit
Release / release (push) Successful in 58s
Release / release (push) Successful in 58s
Telegram webhook handler crashed with sqlalchemy.exc.MissingGreenlet
when processing any incoming message after committing the chat row:
TelegramChat.bot_id == bot.id
^^^^^^
MissingGreenlet: greenlet_spawn has not been called
AsyncSession expires all instances on commit. Accessing bot.id/bot.token
after that triggers implicit lazy-load I/O from a sync attribute getter,
which can't enter the greenlet dispatcher → crash.
Fix: snapshot bot.id + bot.token to locals before commit, refresh the
ORM instance after a successful commit so handle_command() can still
use it, and route the remaining call sites through the snapshot
variables.
This commit is contained in:
@@ -71,9 +71,15 @@ async def telegram_webhook(
|
|||||||
# Auto-persist chat from incoming message
|
# Auto-persist chat from incoming message
|
||||||
from_user = message.get("from", {})
|
from_user = message.get("from", {})
|
||||||
msg_language = from_user.get("language_code", "")
|
msg_language = from_user.get("language_code", "")
|
||||||
|
# Snapshot bot identity before commit — AsyncSession expires instances
|
||||||
|
# on commit, and implicit lazy-load of `bot.id` / `bot.token` later would
|
||||||
|
# raise sqlalchemy.exc.MissingGreenlet.
|
||||||
|
bot_id = bot.id
|
||||||
|
bot_token = bot.token
|
||||||
try:
|
try:
|
||||||
await save_chat_from_webhook(session, bot.id, chat_info, language_code=msg_language)
|
await save_chat_from_webhook(session, bot_id, chat_info, language_code=msg_language)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
await session.refresh(bot)
|
||||||
except Exception:
|
except Exception:
|
||||||
_LOGGER.warning("Failed to auto-save chat %s", chat_id, exc_info=True)
|
_LOGGER.warning("Failed to auto-save chat %s", chat_id, exc_info=True)
|
||||||
|
|
||||||
@@ -81,7 +87,7 @@ async def telegram_webhook(
|
|||||||
if text.startswith("/"):
|
if text.startswith("/"):
|
||||||
chat_row = (await session.exec(
|
chat_row = (await session.exec(
|
||||||
select(TelegramChat).where(
|
select(TelegramChat).where(
|
||||||
TelegramChat.bot_id == bot.id,
|
TelegramChat.bot_id == bot_id,
|
||||||
TelegramChat.chat_id == chat_id,
|
TelegramChat.chat_id == chat_id,
|
||||||
)
|
)
|
||||||
)).first()
|
)).first()
|
||||||
@@ -93,9 +99,9 @@ async def telegram_webhook(
|
|||||||
if responses:
|
if responses:
|
||||||
for resp in responses:
|
for resp in responses:
|
||||||
if resp.text:
|
if resp.text:
|
||||||
await send_reply(bot.token, chat_id, resp.text, reply_to_message_id=message_id)
|
await send_reply(bot_token, chat_id, resp.text, reply_to_message_id=message_id)
|
||||||
if resp.media:
|
if resp.media:
|
||||||
await send_media_group(bot.token, chat_id, resp.media, reply_to_message_id=message_id)
|
await send_media_group(bot_token, chat_id, resp.media, reply_to_message_id=message_id)
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|
||||||
return {"ok": True, "skipped": "not_a_command"}
|
return {"ok": True, "skipped": "not_a_command"}
|
||||||
|
|||||||
Reference in New Issue
Block a user