You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

discord.py Timeout context manager需在task内运行报错求助

问题描述

我最初以为Timeout context manager should be used inside a task这个报错是因为代码没有运行在async def定义的异步函数内,但我的相关逻辑确实都写在异步函数里。我先执行了loop = asyncio.new_event_loop()创建事件循环,之后通过loop.run_until_complete(onInfoAsync(info,conn))执行异步逻辑,具体报错信息如下:

Timeout context manager should be used inside a task
  File "C:\Users\ianfi\Documents\pythonStuff\PyQt5\ServerCalling\main.py", line 42, in onInfoAsync
    dm = await member.create_dm()
  File "C:\Users\ianfi\Documents\pythonStuff\PyQt5\ServerCalling\main.py", line 57, in onInfo
    loop.run_until_complete(onInfoAsync(info,conn))

从报错栈可以看到,错误最终追溯到loop.run_until_complete(onInfoAsync(info,conn))调用处,按我的理解这个调用本来应该把协程包装成任务运行,暂时没找到问题根源。我是Stack Overflow新用户,如果排查需要更多信息可以随时告知。

我已经尝试过以下解决方案:

  • 网上有方案提出用asyncio.get_event_loop()替代手动新建事件循环的逻辑可以修复问题,但实际执行时报错提示当前没有可用的事件循环可以获取
  • 尝试调用asyncio.set_event_loop(loop)绑定新建的事件循环,问题没有解决

感谢大家的帮助!

问题补充

后续我做了进一步尝试,编写了一个中间协程函数:

async def between(info:str, conn):
    task = asyncio.create_task(onInfoAsync(info,conn))
    await task

即便已经显式通过asyncio.create_task创建任务(和报错提示要求的“逻辑运行在任务内”一致),错误仍然存在,使用该中间函数后的报错栈如下:

Timeout context manager should be used inside a task
  File "C:\Users\ianfi\Documents\pythonStuff\PyQt5\ServerCalling\main.py", line 51, in onInfoAsync
    dm = await member.create_dm()
  File "C:\Users\ianfi\Documents\pythonStuff\PyQt5\ServerCalling\main.py", line 36, in between
    await task
  File "C:\Users\ianfi\Documents\pythonStuff\PyQt5\ServerCalling\main.py", line 75, in onInfo
    loop.run_until_complete(between(info,conn))

问题原因

这个报错是discord.py库内部超时上下文的校验逻辑导致的:loop.run_until_complete()直接接收协程作为参数时,传入的入口协程不会被自动包装为asyncio.Task实例,只是作为裸协程在事件循环中启动运行。member.create_dm()内部的超时管理器会校验当前执行上下文是否处于Task环境,你在协程内部调用asyncio.create_task()创建的子任务虽然是合法Task实例,但最外层执行上下文始终是裸协程,校验自然不通过,套一层中间协程也没用,和是否手动绑定事件循环、用不用get_event_loop没有直接关系。

修复方法

不要直接把业务协程传给run_until_complete,显式用loop.create_task()把入口协程包装成Task实例再传入即可,不需要额外写中间协程。
修改你的调用代码:

loop = asyncio.new_event_loop()
# 必须先把新创建的循环绑定到当前线程,否则后续asyncio接口调用会出问题
asyncio.set_event_loop(loop)
# 显式将入口协程包装为Task
entry_task = loop.create_task(onInfoAsync(info, conn))
loop.run_until_complete(entry_task)

如果你要保留between中间层的写法,只需要把传给run_until_complete的对象换成loop.create_task(between(info,conn))即可,效果一致。

补充说明

你之前调用asyncio.get_event_loop()报错,是因为PyQt5主线程默认没有初始化asyncio事件循环,手动创建新循环后只要先执行asyncio.set_event_loop(loop)把循环绑定到当前线程,后续再调用asyncio.get_event_loop()就能正常获取到这个循环实例,不会再抛错。


内容的提问来源于stack exchange,提问作者ianfinity

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 06:22:03