> 来源:arte.itlibra.com
> 原文:https://arte.itlibra.com/en/articles/claude-code-court-invoke-tool-call-error
> 一句话:Claude Code 用户可能突然看到终端里出现一个毫无意义的词 court,后面跟着一串 标签。这不是你搞错了什么——这是一个模型侧的生成 bug,而且最危险的不是它「出错」,而是它会「传染」。
—
一、现象:你的终端突然说「court」
如果你用 Claude Code 做过长会话,可能遇到过这种诡异场景:
court ssh host ‘cp file file.bak ; sed -i …’ …description of the command…
Your tool call was malformed and could not be parsed. Please retry.
一个应该在后台静默执行的 Bash 命令,突然以原始 XML 的形式泄露到了你的屏幕上。前面还莫名其妙地站着一个单词:court(或者 call)。
你的第一反应可能是: – 我环境配置错了? – 我的命令写坏了? – Claude 被黑了?
答案:都不是。这是 Claude(特别是 Opus 4.8/4.7 家族)的一个已知模型侧生成 bug。
Anthropic 官方仓库里堆满了关于这个问题的 issue:#64108、#64150、#64690、#65705、#66153、#67295、#68354……多到可以开一个专题了。
—
二、技术机制:工具调用不是 API 调用,而是「文本生成」
你以为的过程
用户:帮我备份文件 Claude 内部:调用 Bash 工具 → 执行 → 返回结果 你看:备份完成
实际的过程
用户:帮我备份文件 Claude 内部生成文本: “让我先备份文件…… cp file file.bak ” Claude Code(客户端):解析这些 XML 标签 → 提取命令 → 实际执行 → 把结果塞回对话 你看:备份完成
关键洞察:工具调用对 Claude 来说,本质上就是生成一段带有特殊控制标签的文本。这些标签(、)和普通的英文单词一样,都是一个一个 token 生成出来的。
正常情况下,客户端(Claude Code)会解析这些标签,提取命令执行,然后把执行结果呈现给用户。标签本身永远不应该出现在屏幕上。
当 court 出现——控制 token 损坏了
问题出在这个序列的第一个控制 token。在生成 <invoke 这个标签的时候,模型因为某种采样抖动,生成了一个相邻的 token——court(或者 call)——而不是正确的标签开头。
结果,整个工具调用的「信封」被破坏了:
原本应该是: …
实际变成了: court …
Claude Code 的解析器看到 court 开头,不认识这是什么东西,判定「这不是一个工具调用,只是普通文本」,于是直接把它流式输出到屏幕。
命令没有执行。没有安全风险。因为解析器是「fail-closed」的——不认识就拒绝,而不是猜着执行。
—
三、为什么这个 bug 不是小事:「链式反应」
如果事情只是「偶尔出现一个 court,然后重试就好」,那这个问题不值得专门写一篇文章。
真正的问题是:这个 bug 会传染。
核心机制:Self-Poisoning(自我污染)
当一个破损的工具调用块留在对话历史中,Claude 在后续生成时,会把它当成一个「正确的示例」来模仿。也就是说:
第一轮:模型偶然生成了 court + 破损调用 第二轮:模型看到历史里有 court + 破损调用,认为「这是正确的格式」 第三轮:模型再次生成 court + 破损调用 第四轮:……
Issue #64150 和 #62344 记录了这种现象:一旦链式反应开始,重试不会修复,而是让错误模式被不断强化。
真实案例
– Issue #65705:用户在一个 1,739 行、持续 3 天的会话中,连续遇到 14 次失败 – Issue #66153:一个会话中连续 30+ 次失败
这就是为什么文章给出的铁律是:
> Miss twice, bail to a fresh session.
> 出错一次,可以重试一次。连续出错两次,立刻 /clear 开新会话。
—
四、触发条件:什么情况下容易中招?
根据 issue 的统计,以下场景会提高触发概率:
1. 超长会话:多天的持续会话,百万级上下文(#65705:1,739 行,3 天)
2. 单次消息多个工具调用:一次请求里塞了太多工具调用
3. 工具调用紧跟在 prose 后面:文本和工具调用混在一起
4. /compact 后:压缩上下文后的第一次调用(#67295)
5. 后台 Bash(run_in_background) 或 3+ 个 MCP 服务器 连接(#64690)
6. 超长的工具参数:参数长度达到段落级别(#49747)
结论:触发条件是「高复杂度环境」,但根本原因是模型侧采样时的 token 生成错误,不是用户做错了什么。
—
五、修复策略:用户和开发者该怎么做
用户侧(Claude Code 用户)
优先级 1:立即开新会话(/clear)
当 court 出现时,不要犹豫,/clear 或新会话。切断被污染的对话历史是最可靠的修复。
优先级 2:重试一次(仅适用于首次出错) 如果这是第一次出现,且之前会话一直正常,可以打字「resume」或「continue」让模型重新生成。但如果连续两次失败,立刻执行优先级 1。
优先级 3:避免高危模式
– 不要在一个会话里跑多天的任务
– 不要把太多工具调用塞进一条消息
– 不要依赖 /compact 作为万能修复(它有时反而触发问题)
– 经常用 git commit 或 .md 交接笔记保存状态,这样随时切换会话无成本
开发者侧(使用 Claude API/SDK 构建 Agent)
如果你是开发者,在自己的 harness 中可以增加以下防御:
// 检查收到的 assistant 回复中是否泄露了工具调用标记 const text = assistantText(resp); const looksLeaked = /<invokes+name=/.test(text) || /function_calls/.test(text) || /bcourts*<invoke/.test(text);
if (looksLeaked && !hasToolUseBlock(resp)) { // 1. 不要展示给用户 // 2. 不要保留在对话历史中(防止 self-poisoning) // 3. 提示模型重新以结构化 tool_use 格式生成 return retryWithNudge(messages); }
// 检查 stop_reason:如果 max_tokens 截断在 tool_use 块中间,不要执行 if (resp.stop_reason === ‘max_tokens’ && lastBlock(resp)?.type === ‘tool_use’) { return retryWith({ max_tokens: higher }); }
四个核心原则:
1. 始终检查 stop_reason
2. 检测文本中的 <invoke name= 或 function_calls 泄露,发现则重试
3. 不要把破损的调用块留在对话历史中
4. 保持工具参数简短,避免长 payload 变体
—
六、常见误解纠正
| 常见误解 | 真相 |
|---|---|
| 「工具失控了,命令可能乱执行」 | 相反。 破损调用被判定为 malformed,拒绝执行(fail-closed)。没有命令会运行,没有安全风险。 |
| 「重试就行,它会自愈」 | 只对一半。 首次出错重试通常有效,但一旦链式反应开始,同会话内重试会加深污染。 |
「court 有特殊含义,是某种命令」 | 没有任何含义。 只是损坏控制 token 的「签名」。但它作为诊断标记很有用——看到 court 就知道是这个 bug。 |
—
七、官方状态:截至 2026 年 6 月,尚无确认修复
这是一个令人不安的事实:
> 截至 2026 年 6 月,没有官方 changelog 确认这个问题已被修复。
Claude Code 的 2.1.183 版本及之前,没有针对工具调用序列化损坏的修复条目。大量相关 issue 仍被标记为 open 或 duplicate/stale。
但这里有一个关键的安全设计值得肯定:Claude Code 的解析器是「fail-closed」的——拒绝执行一个损坏的调用,而不是试图猜着修复并执行。后者会更危险:如果解析器「聪明地」修复了一个损坏的命令并执行,它可能运行一个错误甚至恶意的命令。
需要修复的是模型的生成稳定性,而不是放宽解析器的严格性。
—
八、延伸:这与 MCP 生态的关系
MCP(Model Context Protocol)是 Anthropic 推动的 Agent 工具标准,正在被广泛采用。这个 bug 之所以重要,不仅因为它影响 Claude Code,更因为:
– MCP 工具调用的底层也是同样的机制:模型生成控制标签,客户端解析执行 – 如果 Claude 的 Opus 家族在这个基础机制上有稳定性问题,任何基于 MCP 的 Agent 框架(OpenCode、Codex 等)如果接入相同模型,理论上都可能遇到类似问题 – harness 设计的「fail-closed」原则,是所有 Agent 框架应该学习的安全模式
—
九、结语:一个「无害但烦人」的 bug,教会我们的工程课
Claude Code 的 court bug 看起来很小:一个无意义的单词,一次失败的工具调用,重试或刷新就解决。
但它揭示了几个重要的工程原则:
1. Fail-closed 是底线:不认识就拒绝,比猜着执行更安全。Claude Code 的解析器在这个 bug 上表现出了正确的安全设计。
2. Self-poisoning 是对话系统的隐形杀手:错误留在历史里会被模仿,这解释了为什么「重试」有时反而让问题更糟。
3. Context 是双刃剑:长上下文给了模型更多能力,但也给了错误更多「复制模板」的机会。/compact 不是万能药,对话历史的清洁度可能比上下文长度更重要。
4. 模型侧的「微小」生成错误,可以放大为用户侧的「系统性」故障:一个 token 的采样抖动(court vs <invoke),最终可能导致 30+ 次连续失败。
对于 Claude Code 用户,记住这个规则就够了:
> 看到 court → 重试一次 → 再失败 → /clear → 保存状态 → 新会话。
对于开发者,记住这个防御模式:
> 检测泄露 → 拒绝保留 → 重试重新生成 → 永远不要猜着执行。
—
参考信息 – 原文分析:https://arte.itlibra.com/en/articles/claude-code-court-invoke-tool-call-error – 相关 Issue:Anthropic 官方 #64108, #64150, #64690, #65705, #66153, #67295, #68354 – 影响模型:Claude Opus 4.8 / 4.7 家族 – 关键概念:Fail-closed harness、Self-poisoning、Tool-call generation corruption – 官方状态:截至 2026-06 无确认修复,workaround 阶段
#ClaudeCode #MCP #ToolCall #Anthropic #AI安全 #FailClosed #SelfPoisoning #Bug分析 #小凯
