这是Python 3.7+调试模式下因协程未被await、create_task或ensure_future调度而触发的RuntimeWarning。常见于同步函数中直接调用异步函数、顶层未用asyncio.run、条件分支遗漏await、列表推导式生成协程对象等场景。

RuntimeWarning: coroutine 'xxx' was never awaited 是什么问题
这是 Python 3.7+ 在调试模式下抛出的运行时警告,不是错误,但意味着你写了一个 async def 函数,却像普通函数一样直接调用了它——比如写了 my_coro() 而没写 await my_coro()。Python 创建了协程对象,但你既没 await 它,也没用 asyncio.create_task() 或 asyncio.ensure_future() 显式调度它,导致它被创建后立刻丢弃。
哪些地方最容易漏掉 await
常见于以下几类误用:
- 在同步函数里直接调用异步函数(比如在
__init__、print()参数中、或日志语句里写get_data()而非await get_data()) - 忘记
await顶层协程调用,例如在if __name__ == '__main__':下写了main()而不是asyncio.run(main()) - 条件分支中只在部分路径里
await,比如if x: await f() else: f()—— 后者就漏了 - 用
map()或列表推导式批量调用异步函数:[fetch(url) for url in urls]得到的是协程对象列表,不是结果
怎么快速定位和修复
启用 -Werror 把警告当错误跑一次:python -Werror::RuntimeWarning script.py,能立刻让程序因未 await 崩掉,从而暴露位置。修复方式取决于上下文:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 如果当前函数是
async的,直接补await:把do_something()改成await do_something() - 如果当前函数是同步的,不能用
await,就得用asyncio.run()包一层(仅限最外层,如脚本入口),或改用asyncio.get_event_loop().run_until_complete()(Python - 如果是想并发执行多个协程,别用列表推导式,改用
await asyncio.gather(*[f(x) for x in xs])或await asyncio.gather(f(1), f(2), f(3)) - 如果只是想“触发但不等结果”(fire-and-forget),显式转成任务:
asyncio.create_task(my_coro()),否则会被 GC 掉且可能根本没执行
为什么有时不报警告,有时又报
警告只在协程对象被垃圾回收时触发,而 GC 时机不确定。所以:
立即学习“Python免费学习笔记(深入)”;
- 在短生命周期脚本里可能压根不报(协程还没来得及被回收程序就结束了)
- 在长期运行的 Web 服务(如 FastAPI、aiohttp)中,每次请求都漏
await,大概率会稳定复现警告 - 使用
pytest-asyncio时,测试函数标记为@pytest.mark.asyncio后,框架会自动await,但如果你在测试内部又漏掉一层,警告仍会出现 -
asyncio.run()内部做了清理,但不会帮你检查子调用是否漏await
别依赖“没警告=没问题”,协程漏 await 是静默逻辑错误,结果不可预测。最稳妥的做法是:所有 async def 函数返回值,只要不是明确要传给 gather/create_task,就该有且仅有一个 await 点。

















