Web Chat: history fails to load ("received more than one start Match") when the model reuses tool-call IDs across steps #4501
Replies: 4 comments
|
I'm facing the same issue |
|
对,这是 Web Chat 折叠,不是 dsh-session-surgeon 不会给第二次 call 换假 id,也不会删重复 start——result 对不上就会变成悬空 tool_call / 永久 400。 要恢复聊天只能:用 Trajectory、开新会话(⋯ → 复制会话 ID 贴过去接着学),或等官方 matcher 把同 id 的第二次 start 当 update / 拒绝写入 |
|
Same root cause here. Wanted to add data on which setups trigger it, because I think it narrows where the fix belongs. I scanned 13 session logs under
The rows that matter are the kimi-k3 block: the same model is fine behind one gateway and broken behind another. So this isn't model behavior — it's that dsh has no fallback for a gateway that omits
Nothing in between promotes the ID to session-global uniqueness, so the contract breaks. Two more observations that may help:
Suggested fix: either scope the context key by turn/step, or have the normalization layer mint globally unique IDs when the gateway doesn't supply one. |
|
这个复现已经把 provider/gateway 的 ID 生成与 Web Chat replay 的严格 matcher 两层区分出来了。恢复时建议把原始 Session 当作只读证据,不要为了让 Chat 能加载而直接重编号或删除 tool/call:
如果 issue 作者补充 provider/gateway 的原始 tool-call ID 是否为空、何时被合成,以及 call/result 成对映射,会更容易决定修复位置。独立社区手册的 Session 完整性与展示投影边界:https://github.com/sandbaseai/deepseek-harness-handbook/blob/main/docs/en/troubleshooting/session-history-corruption-triage.md 。手册不是 DeepSeek AI 官方项目。 |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Summary
When a session's history is replayed into the web Chat view, the conversation assembler hard-throws if two
tool/callevents carry the samecallId. Some models/providers (e.g.moonshotai/kimi-k3via an OpenAI-completions endpoint) emit per-message tool-call IDs likeedit:0,read:0,pwsh:0— tool name + a per-step index — which restart numbering in every assistant message. Any session that reuses an ID in a later step (very common with edit-retry loops) then becomes permanently unloadable in the Chat view:The session data itself is intact — the Trajectory view (separate node definitions) still renders fine and the session JSONL is well-formed — but the Chat view only shows a stale partial tail (whatever was live-streamed before the last reload) plus the red banner. "Load earlier" cannot recover it either.
Environment
0.1.1-rc.2(latest release at the time of writing),--profile webmoonshotai/kimi-k3served through a third-party OpenAI-completions-compatible aggregator endpoint (provider name withheld for privacy)Evidence from a real session
A real session reached 13 turns / 233 steps / 271 tool calls. Because call IDs restart per step, IDs collide within a turn:
edit:0issued at steps 2, 4, 5, 6, 7, 19, 20, 26, 27, 33, 34 (11 starts)edit:0issued at steps 7–12 (6 starts) → this is theContext 9:tool-calledit0in the errorOn reload, history replay hits the second
tool/callwithcallId: "edit:0"inside turn 9, throws, and everything not already live-rendered is dropped from the Chat view (the visible tail stopped mid-turn-8 — roughly 1.5 h of subsequent work was invisible in Chat while Trajectory showed it fine).Root cause chain
packages/client/ui-conversation/src/client/conversation-nodes/tool.ts—toolDefinition.matchuses the raw callId as the context identity:packages/client/runtime/src/client/sessions/conversation-assembler.ts—acceptMatch()(and the same guard inapplyPendingMatches()) hard-throws on a secondstartfor the same key:Key format from
conversationContextKey():`${kind.length}:${kind}${id}`→9:tool-calledit0(tool-call= 9 chars).The throw escapes per-context replay and aborts the entire history load → the
chat.loadErrorbanner (packages/client/ui-conversation/src/client/locales.ts).The implicit assumption "a callId is globally unique within a session" does not hold for providers that issue per-message sequential IDs.
Suggested fix directions
Any one of these would have saved the session. I understand external PRs are not accepted right now, so this report includes the analysis purely to help the team pick a direction — I'm happy to provide the full session JSONL, extra logs, or repro details if useful:
toolDefinition.match— includeturn/step(both available onevent.datafortool/call) in the id, so per-step-restarting IDs cannot collide.tool/resultupdates need the same scoping (turn/step would have to come from the result event or seq proximity).dsh-llmresponse assembler, rewrite non-unique provider call IDs into session-unique ones (e.g. prefix with a per-session counter). Fixes every downstream consumer at once, keeps the assembler's strictness.Repro
editthat fails once and is retried — even easier: any parallel tool batch in two consecutive steps).Failed to load history … received more than one start Matchbanner and renders only the stale tail; Trajectory view is unaffected.All reactions