快速结论:该报错通常出现在长时间运行的 MCP 客户端中,当 httpx 在请求中断(网络掉线、超时、任务组销毁)时从另一个任务关闭了 OAuthClientProvider.async_auth_flow 生成器,导致 anyio.Lock 的释放发生在非加锁任务上并抛出 RuntimeError: The current task is not holding this lock。锁随后被永久占用,之后所有共用同一个 provider 的请求都会卡在 async with self.context.lock 处且不发出任何 HTTP 请求。
适用环境:已确认环境为 mcp 2.0.0(同样代码存在于 2.1.0 和 main),Python 3.11.15,macOS (darwin),anyio 4.14.2,asyncio 后端。生产报告中提到 CPython 3.11、macOS arm64、anyio 4.x asyncio 后端。使用 OAuth 认证的 streamable-HTTP MCP 服务器。
最快修复方案:暂无确认的一步修复方案。Issue 被官方作为 #2847 的重复项关闭,讨论中提出并验证了将 OAuthContext.lock 从 anyio.Lock 改为 anyio.Semaphore(1) 的方向,但尚未合并到发布版本。
注意事项:Semaphore(1) 方案只在 Issue 讨论的复现实验中被确认可跨任务干净关闭,并非官方已发布修复。该方案会改变释放语义,但由于 OAuthContext.lock 仅被 OAuthClientProvider.async_auth_flow 使用且需要跨 yield 持有,因此讨论认为可以保持在 OAuth context 局部范围内。未经确认的修复可能导致并发流序列化行为变化。
问题场景
用户在运行长时间存活的 MCP 客户端进程(例如托管多个 OAuth streamable-HTTP MCP 服务器的 ACP server,报告中提到 Notion、Composio、Consensus,以及 Nous Research Hermes agent 场景)时触发该问题。典型触发场景是:服务器关闭了 standalone GET 流并触发重连(日志中表现为 GET stream disconnected, reconnecting in 1000ms...),随后会话拆除过程从另一个 asyncio 任务关闭了正在进行的 async_auth_flow 生成器。
报错原文
ERROR asyncio: Task exception was never retrieved
future: <Task finished name='Task-598' coro=<()> exception=RuntimeError('The current task is not holding this lock')>
Traceback (most recent call last):
File ".../site-packages/mcp/client/auth/oauth2.py", line 754, in async_auth_flow
yield request
GeneratorExit
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File ".../site-packages/mcp/client/auth/oauth2.py", line 582, in async_auth_flow
async with self.context.lock:
File ".../site-packages/anyio/_core/_synchronization.py", line 173, in __aexit__
self.release()
File ".../site-packages/anyio/_backends/_asyncio.py", line 1935, in release
raise RuntimeError("The current task is not holding this lock")
RuntimeError: The current task is not holding this lock
同类报错的另一处捕获(authorization-code 交换阶段)除 GeneratorExit 落在 token_response = yield await self._perform_authorization() 外,其余一致。
原因分析
OAuthClientProvider.async_auth_flow 在 v2.0.0 的 src/mcp/client/auth/oauth2.py:582(2.1.0 和 main 中代码相同)处使用 async with self.context.lock: 覆盖了整个 httpx auth-flow 生成器,包括每一次 yield。而 anyio.Lock.release() 绑定到获取该锁的任务。当 httpx 从与推进生成器不同的任务关闭该生成器时(请求被取消、网络中断、超时、任务组销毁时常见),async with 的 __aexit__ 在关闭任务中执行并抛出 RuntimeError: The current task is not holding this lock。该异常逃逸到 fire-and-forget 的拆除任务中(表现为 “Task exception was never retrieved”),锁则被永久保持在已占用状态。
讨论中复现确认:挂起在 async with anyio.Lock() 内的流在另一个 asyncio 任务调用 aclose() 时抛出该 RuntimeError,且锁仍保持占用;而同样的流改用 anyio.Semaphore(1) 则可以从另一任务干净关闭,后续流能正常获取信号量。OAuthContext.lock 只被 OAuthClientProvider.async_auth_flow 使用,因此该修改被认为可以保持在 OAuth context 局部范围内。
环境排查
- 确认
mcp版本是否为 2.0.0,或是否有 2.1.0 / main 中包含相同async with self.context.lock模式的代码。 - 确认 Python 版本(报告为 CPython 3.11.15 / 3.11)。
- 确认 anyio 版本(报告为 4.14.2)及后端(asyncio)。
- 确认操作系统与架构(报告为 macOS darwin / macOS arm64)。
- 确认是否使用了 OAuth 认证的 streamable-HTTP MCP 服务器,以及是否为长时间存活且复用同一个
OAuthClientProvider的进程。 - 确认日志中是否出现重连提示(如
GET stream disconnected, reconnecting...)后紧跟Task exception was never retrieved。
解决步骤
- 先通过日志确认出现了
RuntimeError: The current task is not holding this lock,且指向mcp/client/auth/oauth2.py中的async with self.context.lock。这是定位该缺陷的关键线索。 - 确认现象:后续对受影响服务器的连接尝试卡在
async with self.context.lock处,没有任何 HTTP 流量(报告中通过 socket 检查确认无 SYN,仅存在 CLOSE_WAIT),直到达到连接超时(报告中为 300s)。 - 确认毒化状态是否局限于单个进程:从全新进程连接同一 OAuth 服务器应能正常握手(报告中小于 1s 完成 initialize + list_tools),说明 token、网络、服务器本身无问题。
- 作为已确认有效的临时缓解措施:重启该进程。报告中手动 kill 进程后问题完全解决。注意其他客户端进程的重启不会影响被毒化的进程,因为该状态是每进程内存态。
- 若需从代码层面修复,可优先尝试讨论中提出的方向:将
OAuthContext.lock从anyio.Lock改为anyio.Semaphore(1),使释放路径不再假设释放任务是获取任务。该方向在复现实验中被验证可跨任务干净关闭。此方案未在 Issue 中合并到发布版本,属于可优先尝试的修改。 - 配套回归测试应覆盖生成器生命周期而非仅检查字段类型:从一个任务推进
async_auth_flow至 yield,从第二个任务关闭它,再证明后续流能继续执行;测试使用仓库的 anyio 风格并用fail_after限定等待时间,覆盖跨任务拆除路径。
验证方法
修复后应确认:从非加锁任务关闭正在挂起的 async_auth_flow 生成器时不再抛出 RuntimeError: The current task is not holding this lock,并且后续同一 provider 的 async_auth_flow 能正常获取并执行,连接不再卡在 async with self.context.lock 处。可参照讨论中的最小复现(无网络需求)验证 provider.context.lock.locked() 在关闭后不再保持占用。
参考来源
modelcontextprotocol/python-sdk #3382
官方将本 Issue 作为重复项关闭,并指向 #2847。
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


![RuntimeError: [json.exception.type_error.302] type must be number, but is number](https://www.chat-gpts.plus/wp-content/uploads/2026/10/1251-6c29e56c-768x403.jpg)