使用Aiogram 3.7b开发Telegram Bot遇SyncToAsync协程未等待错误
问题根源
报错的核心原因是get_parents_student_keyboard函数的分支返回值不符合预期:
当学生数量为1时,代码直接返回get_date_chooser_parents(tg_bot_auth)的调用结果。如果get_date_chooser_parents是被@sync_to_async装饰的异步函数,这个调用会返回协程对象,而非预期的(keyboard_markup, text, state_next)三元组。
当get_parents_student_keyboard(被@sync_to_async包装的同步函数)返回该协程对象时,handler中await后得到的仍然是协程对象,无法解包为三个变量,因此触发TypeError;同时协程未被正确执行,引发RuntimeWarning。
解决方案
根据get_date_chooser_parents的类型选择对应修改方式:
方案1:get_date_chooser_parents为同步函数
确保get_date_chooser_parents未被@sync_to_async装饰,直接返回(键盘标记, 文本, 状态)三元组。此时get_parents_student_keyboard的代码无需修改,调用后会正常返回符合要求的三元组。
方案2:get_date_chooser_parents为异步函数
将get_parents_student_keyboard改为异步函数,移除@sync_to_async装饰器,内部通过await获取get_date_chooser_parents的返回值,同时同步ORM操作改为异步方式:
async def get_parents_student_keyboard(tg_bot_auth: TGBotAuth): parent = await tg_bot_auth.user.aget() # 异步关联查询 students = await Student.objects.filter(parent=parent).all() builder = ReplyKeyboardBuilder() if len(students) == 1: # await异步函数,获取正确的三元组返回值 return await get_date_chooser_parents(tg_bot_auth) for student in students: builder.button(text=str(student)) return builder.as_markup(), MESSAGES['parents_students'], ParentsState.student_choosing
handler中的调用保持await get_parents_student_keyboard(tg_bot_auth)不变即可。
额外注意事项
- 使用Django ORM时,异步函数内必须用异步查询方法(如
aget()、all().await()),不能直接使用同步查询语法。 - 确保函数所有分支都返回三元组,避免返回其他类型对象导致解包失败。
内容的提问来源于stack exchange,提问作者Shuhrat Azimuratov

