记一次 AI 编程助手报错的完整排查过程:tool call id 重复导致请求失败怎么办?
如果你在使用 AI 编程助手(比如 openclaw)时,突然看到这样一行报错:
HTTP 400: Invalid request: tool call id exec:29 is duplicated
先别慌。这个错误看起来很技术,但背后的原因其实并不复杂。这篇文章会带你从零开始,彻底搞清楚它为什么会发生、如何定位、怎么修复,以及以后怎么避免。
这个报错到底在说什么?
AI 助手在帮你执行任务时,会调用一系列”工具”(Tool),比如执行代码、读取文件、搜索网络等。每次调用工具,系统都会生成一个唯一的标识符,也就是 tool call id(工具调用 ID)。
问题出在哪里?当 AI 在一次对话历史里,出现了两条使用相同 ID 的工具调用记录,部分大模型 API 就会直接拒绝这个请求,返回 400 错误。
具体到这个案例:
-
报错内容: tool call id exec:29 is duplicated -
使用的模型: kimi-k2.5 -
服务商: moonshot(即 Kimi 的 API) -
工具平台:openclaw
翻译成人话就是:对话历史里出现了两个 ID 都叫 exec:29 的工具调用,Kimi 的 API 不允许这种情况,直接返回了错误。
为什么偏偏是 Kimi 报错,其他模型没事?
这是很多人困惑的地方。同样的操作,换成 Anthropic(Claude)或者 OpenAI 就没问题,但一切换回 Kimi 就报错。
原因在于:不同的大模型 API 对请求格式的校验严格程度不同。
Kimi 的 API 对 tool call id 的唯一性做了严格校验,只要在同一段对话历史中出现重复 ID,就会拒绝整个请求。而其他服务商的 API 可能对这个细节做了容错处理,不会直接报错。
所以这个问题不是 Kimi 的 bug,而是 openclaw 在生成 tool call id 时出现了 bug——重试逻辑触发时,ID 计数器没有正确递增,导致新生成的 ID 与历史记录里的 ID 重复。
问题是怎么一步步出现的?
根据完整的日志分析,这个 bug 的触发过程大致如下:
第一步:正常调用
AI 助手正常执行了一次文件操作,生成了工具调用 exec:29,结果成功写入对话历史。
第二步:任务失败,触发重试
由于某个操作失败(比如文件路径不存在),openclaw 自动触发了重试机制。
第三步:ID 计数器没有递增
重试时,openclaw 应该生成 exec:30,但由于 bug,它又生成了 exec:29。
第四步:对话历史被污染
新的 exec:29 和旧的 exec:29 同时存在于对话历史中。每次发起新的请求,这段被污染的历史都会一起发送给 API。
第五步:Kimi 拒绝请求
Kimi 的 API 检测到历史中有重复 ID,返回 400 错误。
第六步:无限循环
由于对话历史是持久化存储的,即使重启 gateway、换个问题重试,只要还在同一个 session 里,错误就会一直出现。
这就解释了为什么日志里显示——不同的 runId 报的都是同一个错误 exec:29 is duplicated,重启也没用。
如何确认问题所在(排查步骤)
下面是完整的排查过程,适合在 Windows 环境下操作。
第一步:找到包含重复 ID 的 session 文件
openclaw 的对话历史以 .jsonl 格式存储在本地,每行是一条记录。先用下面的命令搜索哪个文件里包含了 exec:29:
Get-ChildItem -Recurse "C:\Users\你的用户名\.openclaw" -Filter "*.jsonl" |
Select-String "exec:29" |
Select-Object Filename, Path
如果某个文件名反复出现,那它就是问题所在。
第二步:统计重复情况
确认文件后,统计其中 toolCall 类型且包含 exec:29 的行:
$file = "C:\Users\你的用户名\.openclaw\agents\main\sessions\你的session文件.jsonl"
$i = 0
Get-Content $file | ForEach-Object {
$i++
if ($_ -match "exec:29" -and $_ -match "toolCall") {
"行 $i id: $(($_ | ConvertFrom-Json).id) parentId: $(($_ | ConvertFrom-Json).parentId)"
}
}
正常情况下,toolCall 类型的 exec:29 应该只出现一次(第一次调用)。如果你看到它出现了多次,就说明 session 已经被污染。
第三步:确认对话历史结构
看看原始的那条记录长什么样:
$lines = Get-Content $file
$lines[64] | ConvertFrom-Json | ConvertTo-Json -Depth 3
$lines[65] | ConvertFrom-Json | ConvertTo-Json -Depth 3
正常的结构是这样的:
-
行 65: role: assistant,包含toolCall,id 为exec:29 -
行 66: role: toolResult,返回工具执行结果,引用exec:29
这两行是一对,是正常的。问题在于后续的重试又生成了第二个 exec:29。
修复方法
方法一:隔离被污染的 session(推荐)
最简单也最安全的方式,是把问题 session 文件重命名,让 openclaw 不再读取它:
$file = "C:\Users\你的用户名\.openclaw\agents\main\sessions\你的session文件.jsonl"
# 备份并隔离,不直接删除,保留历史数据
Rename-Item $file "$file.broken"
然后重启 openclaw gateway,新建一个对话,就可以正常使用了。
方法二:如果想恢复历史对话
如果这个 session 里有重要的对话记录,可以先恢复:
Rename-Item "$file.broken" $file
恢复后,切换到其他 provider(如 Anthropic 或 OpenAI)继续使用这个 session。因为其他 provider 对重复 ID 做了容错处理,不会报错。
方法三:手动修复 session 文件(进阶)
如果你有一定技术基础,可以直接编辑 .jsonl 文件,找到第二个 exec:29 的 toolCall 行并删除。但这需要对 jsonl 格式和 openclaw 的数据结构有一定了解,操作前务必备份。
操作总结对照表
| 场景 | 推荐操作 |
|---|---|
| 报错后想快速恢复使用 | 隔离 session 文件,新建对话 |
| 想继续使用旧对话记录 | 恢复文件,切换为其他 provider |
| 想彻底修复文件 | 手动编辑 jsonl,删除重复的 toolCall 行 |
| 想防止再次出现 | 向 openclaw 官方提交 bug 报告 |
在 Windows PowerShell 里找文件:grep 的替代方案
很多教程里用 grep 命令搜索文件内容,但 Windows 的 PowerShell 默认不支持 grep。如果你直接输入,会看到这个错误:
grep : 无法将"grep"项识别为 cmdlet、函数、脚本文件或可运行程序的名称。
PowerShell 里对应的命令是 Select-String,用法如下:
# 在所有 .go 文件中搜索关键词
Get-ChildItem -Recurse -Filter "*.go" | Select-String "exec:"
# 在多种文件类型中搜索
Get-ChildItem -Recurse -Include "*.go","*.ts" | Select-String "exec:"
# 搜索并显示关键词前后各 3 行的上下文
Get-Content "文件路径" | Select-String -Context 3,3 "关键词"
记住:PowerShell 里 Select-String = Linux/Mac 里的 grep。
这类问题的根本原因是什么?
从技术角度来说,这类 bug 通常出现在以下几个场景:
场景一:重试时 ID 计数器被重置
agent 在某次执行失败后,触发重试机制。如果重试逻辑直接复用了上一次的请求上下文,而没有生成新的 ID,就会产生重复。
场景二:对话历史拼接出错
在多轮对话中,如果同一条 assistant 消息(包含 toolCall)被意外追加了两次到历史记录里,也会导致重复。
场景三:流式响应处理有缺陷
在流式接收模型输出时,如果处理逻辑存在缺陷,同一个 toolCall block 可能被重复收集和写入。
openclaw 这个案例属于第一种:重试时 ID 没有正确递增。
常见问题解答
Q:为什么重启 openclaw 之后问题还在?
A:因为对话历史是持久化存储在本地 .jsonl 文件里的。重启 gateway 不会清除历史,下次打开同一个 session,被污染的历史还会被读取出来,继续触发同样的错误。
Q:这是 Kimi API 的问题还是 openclaw 的问题?
A:openclaw 的问题。ID 重复是 openclaw 生成 ID 的 bug 导致的;Kimi 的 API 只是比其他服务商检验更严格,才把这个 bug 暴露出来。换句话说,Kimi 的做法是正确的,openclaw 的代码有 bug。
Q:换成 Claude 或 GPT 就不报错,是不是说明 Kimi 兼容性差?
A:不是。Claude 和 GPT 的 API 对重复 ID 做了容错处理,不会报错,但这不代表重复 ID 是合法的请求。Kimi 严格按照规范执行,反而更规范。
Q:这个 bug 会影响我的数据吗?
A:不会丢失数据。问题只在于 API 请求被拒绝,本地的对话历史文件是完整的,只是包含了一条格式有问题的记录。
Q:以后怎么避免这个问题?
A:从用户角度,目前只能等待 openclaw 官方修复这个 bug。你可以去 openclaw 的 GitHub 或官方社区提交 issue,把 exec:29 is duplicated 这个关键信息附上,帮助开发团队定位问题。
Q:.jsonl 文件是什么格式?可以直接用记事本打开吗?
A:.jsonl 是 JSON Lines 格式,每一行都是一个独立的 JSON 对象。可以用记事本打开,但内容很长,建议用 VS Code 等编辑器,并安装 JSON 格式化插件,阅读起来更方便。
写在最后
这次排查过程的核心思路是:
-
不要被表面现象迷惑——报错信息说 ID 重复,就去找重复在哪里 -
日志是最好的线索——永远是同一个 exec:29,说明问题出在持久化数据,而不是运行时逻辑 -
最小化破坏原则——不直接删除文件,而是重命名隔离,保留恢复的可能性 -
工具要用对——Windows 下没有 grep,但有功能一样的Select-String
对于大多数用户来说,遇到这类问题时不需要深入研究源码,只需要能够定位问题文件、执行隔离操作,就能快速恢复正常使用。
如果你在使用其他 AI 编程助手时也遇到类似的 tool call id duplicated 报错,排查思路是一样的:找到持久化的对话历史文件,搜索重复的 ID,隔离或修复对应的 session。
