DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

AnyIO TaskGroup 包装 TimeoutError,外层 except TimeoutError 为什么没接到

AnyIO 4 会把单个普通异常也包装成异常组。展开叶子,用 except* 选择性处理超时,保留清理失败与调用方取消的边界。

异步操作超时,外层已经写了 except TimeoutError,日志却只显示 unhandled errors in a TaskGroup (1 sub-exception)。先展开完整异常树,确认抛到外层的是普通超时,还是包含超时叶子的 ExceptionGroup。

AnyIO 4 的 TaskGroup 会把宿主任务或子任务抛出的普通异常包装成异常组,即使只包含一个异常。except TimeoutError 匹配不了这个外层组;这并不表示所有 except 都失效,普通 ExceptionGroup 仍属于 Exception。

先确认异常叶子和发生阶段

保存完整 traceback、Python、AnyIO 和使用 TaskGroup 的库版本。不要只截“一项子异常”的摘要,也不要根据请求偶尔成功就认定是限流或服务未开。

异常可能来自业务等待,也可能在退出上下文、关闭连接或取消后台任务时发生。连接清理中的错误与业务超时可以同时进入异常树。读取每一层的类型和消息,保留嵌套结构;只取 exceptions[0] 会漏掉其他故障。

确认代码使用的是 AnyIO TaskGroup、标准库 asyncio TaskGroup,还是第三方库封装。相似报错不代表库和版本条件相同。AnyIO 3 与 4 的单异常行为也有变化,不能把新规则倒套到所有旧环境。

用 except* 选择匹配的叶子

Python 3.11 及以上支持 except*。下面是 AnyIO 4 的异常匹配示例:它主动构造一个超时,只演示把匹配子组转换成应用错误,同时保留异常链,不执行真实网络请求:

import anyio


async def sample_operation():
    raise TimeoutError("sample operation timed out")


async def run_operation():
    timeout_group = None
    try:
        async with anyio.create_task_group() as group:
            group.start_soon(sample_operation)
    except* TimeoutError as matched:
        timeout_group = matched

    if timeout_group is not None:
        raise TimeoutError("application operation timed out") from timeout_group

matched 是包含匹配叶子的子组,不是保证只有一项的普通 TimeoutError。这里先保存它,再在处理语句之外抛出应用错误。如果还有不匹配的异常叶子,它们会继续传播,不会被这个处理器解释成超时。

同一个 try 不能同时混用普通 except 和 except*;except* 语句内也不能用 return、break 或 continue。需要普通异常处理时,应明确安排外层结构,而不是放宽为 except BaseException 吞掉取消。

Python 3.11 之前不能直接解析这段语法,应按 AnyIO 支持范围采用 exceptiongroup backport 的处理接口。仅安装 backport,不会让旧解释器识别 except* 语法。

在上下文内部处理后,仍要关注退出错误

自己控制的超时,可以在业务上下文内部捕获、保存状态,并在正常退出和必要清理完成后再抛应用错误。但如果退出清理失败,应保留它的异常,不能因为之前设过超时标记,就把全部清理错误改写成“请求超时”。

在上下文内部捕获 TimeoutError 后立即抛另一个 ValueError,新异常仍可能经过 TaskGroup 再被包装。调整抛出位置解决的是异常传播路径,不会自动修复底层连接、订阅失败或丢失数据。

第三方库已经做了异常翻译时,要核对具体版本的实现及实际运行结果,不凭一条“已修复”说明保证所有请求恢复。尤其应分别检查错误信息是否准确,以及原操作是否取得完整数据,两项不能混为一项。

修改前保存异常处理与依赖组合。在隔离环境检查单超时、其他异常、混合异常组、正常返回和调用方取消:已知超时按约定处理,未匹配错误仍可追踪,取消能按原流程传播,资源完成清理。需要回退时恢复处理边界与依赖,并核对仍在执行的任务,避免重复请求掩盖旧任务的结果。