Repository navigation
Replies: 2 comments
|
Both of your observations are true, and that is the interesting part: the local check and the endpoint are counting different things. What the wire actually carriesI drove the shipped
Same table on Why the local pairing check passes
That content is produced by the same file's own
So a result whose every block is dropped is serialized as Why your offline rebuild cannot find itYour rebuild walks the same messages through the same rules and asks the same id-shaped question, so it answers "paired". It agrees with the serializer because it borrowed the serializer's question — reconciling the same data on the same dimension is a tautology, not evidence. The fact that differs is on the wire, not in the log. What produced the emptiness in your caseTwo shapes reach it, and both are ordinary:
Looking at the tool calls immediately before the failing message in your session log should show which one it was. Mitigation and the real fixThe real fix belongs in the adapter: a Until then, I published a plugin that makes the request legal without touching the session log: @argszero/dsh-empty-tool-result-guard. npm install github:argszero/dsh-empty-tool-result-guard#ac14655It listens on the Verified against all ten released If it turns out your failing message is not this shape — for example the result was there and the call id differed by a normalization step — the plugin will stay silent on it and the |
|
Cross-referencing #3046 (user-side ordering) and #10186 / #9239 (history shape + stricter endpoint), I filed the assistant-side counterpart: #11115. Short version, with the isolating measurement: the same history is accepted or rejected purely by block order —
So |
Uh oh!
There was an error while loading. Please reload this page.
Date: 2026-10-19
Severity: High(原会话切换后无法继续)
Reproducibility: 原会话观察到一次;离线配对检查可重复。
Summary / 概述
Windows 桌面版同一长会话从自定义 OpenAI Chat Completions provider 切到 deepseek-official 后,服务端以 HTTP 400 报 tool_use 缺少紧随其后的 tool_result。但本地持久化日志存在对应结果,使用已安装版本的 Messages 序列化函数重建时,同一调用也紧跟匹配结果。
这是一次真实故障记录及离线核对;目前尚未获得完整出站请求抓包,也未完成可独立运行的上游最小复现。以下不预设问题发生于 Harness、转换层或官方端点。
Expected Behavior / 预期
已经完成、且具有配对结果的历史工具调用应当可以在切换 provider 后继续使用。若历史无法被目标协议表示,应明确提示不兼容位置及恢复方式。
Actual Behavior / 实际
原会话在第三方端点遇到 413 后,用户填写官方 API key 并切到 deepseek-official,希望继续任务。切换后的第一个请求报工具配对错误,尚未开始新的模型输出。报错点位于早已完成的历史工具调用,并非最近一次 413 对应步骤。
Reproduction Steps / 观察到的操作顺序
以上是本案复现路径,不代表 413、图片或长会话一定是必要条件。
Minimal evidence / 已缩减的证据
调用标识统一替换为 CALL_A,任务正文、路径、工具参数和结果正文均省略。原始记录内两个标识完全相同。
{"type":"assistant/message","seq":212,"surfaceOp":"append","data":{"turn":1,"step":31,"message":{"role":"assistant","content":[{"type":"tool-call","id":"CALL_A","name":"edit","arguments":"[omitted]"},{"type":"text","text":"[omitted: 2008 characters]"}]}}} {"type":"tool/call","seq":213,"data":{"turn":1,"step":31,"callId":"CALL_A","name":"edit","arguments":"[omitted]"}} {"type":"tool/result","seq":214,"surfaceOp":"append","sourceEventSeqs":[213],"data":{"turn":1,"step":31,"message":{"role":"tool","toolCallId":"CALL_A","content":[{"type":"text","text":"[successful edit result omitted]"}],"isError":false}}}离线重建说明:从已安装 app.asar 读取 Messages serialize() 及其 assistant()/toolInput() 辅助函数,输入日志中全部 append surface messages。跨 provider 的 readReplay 返回路径以 undefined 表示;历史图片用占位 file id 表示,不上传图片;此检查没有发起模型请求,也不是原请求抓包。
重建得到 604 条 wire messages,报错指定的 message 下标与本地调用位置一致:
{"index":61,"role":"assistant","content":[{"type":"tool_use","id":"CALL_A"},{"type":"text","text":"[omitted]"}]} {"index":62,"role":"user","content":[{"type":"tool_result","tool_use_id":"CALL_A"}]}本地序列化的配对校验通过,但真实请求被服务端判为缺失结果。这一差异尚需定位,不能据此认定已经证明某一层的根因。
Environment / 环境
Error Output / 原始报错
仅替换调用 ID 与 request_id,其余原文保留:
Impact / 影响
原会话无法通过这次切换继续任务。磁盘上的已生成文件仍在;保留工程并新建空白会话可以绕过历史重放,但需要重新交接任务上下文。本报告未验证修复,也未重写原会话。
Related Files / 相关文件
Notes / 相关讨论及边界
没有附带 API key、用户路径、项目内容、图片或完整会话日志。
All reactions