主会话断流重试后收尾消息重复落库:残段错挂 subagent parentUuid,UI 渲染两个重复气泡
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 395
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
**客户端版本**: 0.1.28
**反馈类型**: bug
---
## 现象
claude-code 主会话一个 turn 的收尾长消息,在聊天 UI 里渲染成两个高度重复的气泡:上面「已工作 10m51s」块正文在词中间截断,下面「已工作 4s」块是完整版,前半正文与上一气泡逐字相同。
示意截图见首条评论:按真实 UI 形态 1:1 重构、正文替换为占位内容的脱敏示意图(真实截图含内部工作内容不外发,必要时可打码提供)。
## 数据层证据(本机数据库核实,典型一例)
会话 `f6fd7351-77be-4184-9465-03e5cc43c471`(claude-code),问题 turn 结束于 2026-08-05T02:29:54Z。同一段收尾文本落库了三条 assistant 消息,同属一个 sdkSessionId(`174bbad4-1eb7-477b-a73e-089b85ab9d49`):
| # | message id | 时间(02:29:xx Z) | 内容 | requestId | parentUuid | cacheRead |
|---|---|---|---|---|---|---|
| 1 | m1uwi7jhph0zfloc57l4wzlw | 50.367 | 截断版,止于一个中文词的中间(非句读处) | msg_vrtx_011CdipCSpPNSbqDrd6q221g | toolu_vrtx_01MCAwNXsTj33iq9nTVEoP8k(该 turn 内正在后台运行的一个 subagent 的 tool-use id) | ≈50.6k |
| 2 | qj575i666gb9524sdhnhmyfp | 53.650 | 12 字续传碎片,拼接后与 #1 截断处严格衔接 | msg_vrtx_011CdipCjGMGeiUB8bnTym7S | 同上(同样错挂) | ≈57.6k |
| 3 | hq8g11mpvs4zbpxsoetr1yr5 | 54.118 | 完整版,唯一带 turnCompleted=true | msg_vrtx_011CdipBf2pnCyaGHBQbnA6Z | 无 | ≈279.7k(主会话体量) |
关键事实:
- #1 + #2 拼接后是 #3 的严格前缀;三条 requestId 互不相同。
- #1/#2 的 cacheRead(≈50–58k)与 #3(≈280k)差一个量级,量级与当时挂着的 subagent 上下文一致;但两条的正文内容明确是主会话的收尾汇报。
- #1/#2 的 usage.outputTokens 都只记了 20,与其实际文本长度明显不符,疑似断流后的残缺计量。
## 复现情况(非孤例:全库扫描命中 11 组)
对本机数据库全量扫描(条件:同会话两条 assistant 消息互为严格前缀、残段≥150 字符、间隔<10 分钟),共命中 11 组,分布在 5 个会话(全部为 claude-code 长对话),时间跨度 2026-07-24 至 08-05:
- 8 组与上表同形态:残段错挂 parentUuid,且 cacheRead 显著小于完整版(如 43.1k vs 381.1k、32.7k vs 489.7k、36.6k vs 602.6k);
- 3 组变体:残段与完整版共享同一 requestId、无 parentUuid,疑似同根因的另一落库形态。
全部 11 组的残段均未被折叠(rewind_at 为空),即都会在 UI 重复渲染。规律:长对话中输出较长消息时更易触发。
## 推断根因(标注:推断,未读代码验证)
主会话流式输出中途中断并重试,恰逢一个后台 subagent 挂在该 turn 上;断流留下的半截正文与续传碎片在落库时被按 parentUuid 错误归账为该 subagent 的产出;UI 渲染时把全部 assistant 记录都画出来,于是出现「截断版 + 完整版」双气泡。「已工作 10m51s / 4s」的时长差也吻合:截断版继承了长跑块的计时,完整版是重试后快速完成的计时。另有 3 组无 subagent 在跑的变体(同 requestId 双落库),说明残段未被覆盖的问题可能不限于 parentUuid 错挂一条链路。
## 期望行为
流式中断重试后,残段应被最终完整消息覆盖或折叠,UI 只显示带 turnCompleted 的完整版;主会话的断流残段不应因同 turn 存在运行中的 subagent 而被错挂到其 parentUuid 下。
## 实际行为
残段与完整版全部落库且均未折叠,UI 渲染出两个高度重复的气泡(一条词中截断、一条完整);本机 11 组均如此。
## 相关 issue(已查重排除)
- #1642 是 Codex agent 重试恢复机制的状态化改造;#1327 是 KIMI tool id 撞车导致的空消息腐蚀;#790 是 Codex 流式重连进度显示。本 issue 是 claude-code 主会话的断流残段落库 + parentUuid 错挂,与上述均不同。
---
**版本区域**: CN
**OS**: darwin x64 (24.6.0)
**界面语言**: zh-CN
Contributor guide
Research direction
No source file or test is named. Start by tracing claude-code streaming retry handling and assistant-message persistence around parentUuid, requestId, turnCompleted, and rewind_at, then reproduce the duplicate-message pattern. Done means the interrupted fragment is covered or folded into the completed message, the parentUuid is correct, and the UI shows only the final bubble.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- electron, typescript
- Domain
- backend, databases, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100