You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
history unavailable for session "session-...": Error: corrupt session log: seq gap in committed region at line 33577 (expected 607335, got 607332)
绕过第一处失败后,后续校验错误继续暴露:
invalid seed event at index 685692: sourceEventSeqs must reference earlier events: 696619 >= current seq 685692
而当日志被修复到可以继续聊天时,消息重建仍可能在 provider 端失败:
Error from provider (Console Go): An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id',
The following tool_call_ids did not have response messages: call_00_eaD6ogYyrP2rVa2M9zmf5887
constrowStart=this.events.length;for(consteventofdecoded){if(event.seq!==this.events.length){constexpected=this.events.length;this.events.length=rowStart;this.issue=newError(`corrupt session log: seq gap in committed region at line ${this.eventLine} (expected ${expected}, got ${event.seq})`);if(decoded.some((candidate)=>candidate.type==="turn/end"))throwthis.issue;return;}this.events.push(event);}
因此每个存储事件的 seq 必须等于其在日志中的位置。压缩用一个摘要节点替换一段 surface 区间(@deepseek-ai/dsh-compaction/lib/index.js L171–172:"successful run replaces the selected surface span with one summary node"),这会删除事件——但区间之后的事件保留了原来的 seq,留下空洞。加载器随即报告 seq gap in committed region。
以 [frame0 = header][frame1 = all events] 重新编码,带 ZSTD_c_checksumFlag(与持久化层使用的参数相同);
用与加载器完全一致的语义从磁盘验证:0 空洞、0 溯源错误。
结果(两个会话现已正常打开并持续可用):
会话
修复后事件数
验证
session-f889db13-…
695,462(用户继续聊天期间增长)
0 空洞、0 溯源错误
session-ac5bd0cc-…
1,594,969
0 空洞、0 溯源错误
所有中间状态的备份均保留在磁盘上;没有任何消息内容丢失(只丢失了压缩本身折叠掉的事件)。
附录 —— 观察到的确切错误串
history unavailable for session "session-f889db13-6662-4dff-810f-ed83725422c0":
Error: corrupt session log: seq gap in committed region at line 33577 (expected 607335, got 607332)
history unavailable for session "session-ac5bd0cc-55d2-46ab-b2fd-926377a20ef4":
Error: corrupt session log: seq gap in committed region at line 87285 (expected 1561208, got 1561205)
SessionPersistenceCorruptionError: stored session "session-f889db13-…" failed validation:
Error: invalid seed event at index 685692: sourceEventSeqs must reference earlier events: 696619 >= current seq 685692
Error from provider (Console Go): Upstream request failed: [invalid_request_error]
An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id',
The following tool_call_ids did not have response messages: call_00_eaD6ogYyrP2rVa2M9zmf5887
中文摘要
问题:DeepSeek Harness 0.1.0-rc.6 中,对 ~80 万 token 的大会话执行强制上下文压缩(/compact)后,重启 host 再打开该会话会报 seq gap in committed region 等错误,历史永远无法加载。
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
声明:本文档由 DeepSeek Harness 中的
DeepSeek V4 Flash模型生成,并由该模型自主完成缺陷排查与修复环境
0.1.0-rc.6(dsh webprofile,npm@deepseek-ai/*包均为0.1.0-rc.6)v24.15.0/compact命令)时约 800k 上下文 token@deepseek-ai/dsh-session-persistence-jsonl、@deepseek-ai/dsh-session、@deepseek-ai/dsh-compaction、@deepseek-ai/dsh-llm-deepseek摘要
被压缩过的大会话在 host 重启后无法再加载。打开时显示:
绕过第一处失败后,后续校验错误继续暴露:
而当日志被修复到可以继续聊天时,消息重建仍可能在 provider 端失败:
三个症状同源于一个根因:压缩事务折叠/删除了事件,但没有重排剩余事件的
seq,也没有重映射sourceEventSeqs/surfaceOp.replace引用,最终产出一份连加载器自己都拒绝的日志。复现步骤
/compact)。dsh web)或在新 host 进程中打开该会话。seq gap in committed region错误打开失败(见上)。我们没有观察到任何恢复路径:会话在之后每次打开时都保持损坏状态。
期望 vs 实际
tool_calls/tool-result 序列,被 provider 以 HTTP 400 拒绝。根因分析
1. 加载器要求
seq从 0 全局连续,但压缩留下了空洞@deepseek-ai/dsh-session-persistence-jsonl/lib/index.js(约 L275–300)中的SessionLogScanner.consumeEventLine用运行计数校验每个解码事件:因此每个存储事件的
seq必须等于其在日志中的位置。压缩用一个摘要节点替换一段 surface 区间(@deepseek-ai/dsh-compaction/lib/index.jsL171–172:"successful run replaces the selected surface span with one summary node"),这会删除事件——但区间之后的事件保留了原来的seq,留下空洞。加载器随即报告seq gap in committed region。直接证据(没有任何外部工具碰过该文件):首次故障出现后立即快照的原始备份(22:27,任何修复尝试之前)在 88,700 行中已包含 1,415 个 seq 空洞;最后一个存储的
seq是 1,594,552,而首个空洞之前只有 1,561,208 个事件。该文件由 DSH 自己写入(压缩 + 后续追加)——它自己的加载器却拒绝读取。第二个会话表现出同样的模式:3,950 个空洞;首个空洞之前有 607,335 个连续事件,而尾部事件携带的
seq为 705,765。2. 压缩未重映射溯源引用(
sourceEventSeqs、surfaceOp.replace)@deepseek-ai/dsh-session/lib/types/surface.js(约 L150–180)中的assertProvenance要求sourceEventSeqs中的每个条目都指向更早的事件:重编号(我的修复)之后,在原编号下合法的引用变得非法:
invalid seed event at index 685692: sourceEventSeqs must reference earlier events: 696619 >= current seq 685692。在修复后的会话中,675,246 个sourceEventSeqs条目全部引用压缩前的 seq 编号;其中 675,245 个干净地重映射成功,1 个引用了被压缩完全折叠掉的事件(悬空)。另有 4 处surfaceOp.replace区间的start/end需要重映射。3. 压缩可能留下孤立的
tool/result,随后破坏消息重建压缩边界切断了 tool 往返:
assistant/message(含tool_calls)和tool/call被折叠,但tool/result行幸存。回放时,@deepseek-ai/dsh-llm-deepseek/lib/index.js(约 L70–98)中的serializeMessages把每个tool-result块变成独立的{role:"tool"}线上消息;由于缺少前置的 assistanttool_calls,provider 以 HTTP 400 拒绝请求(The following tool_call_ids did not have response messages: call_00_…)。这是同一压缩 bug 的第二种表现(配对平衡检查存在于dsh-compaction/lib/index.jsL42–43,但我们的数据中折叠边界仍然产出了未配对的 result)。影响
建议修复
seq使其从 0 全局连续,或让压缩事务重写日志尾部。sourceEventSeqs条目和surfaceOp.replace的start/end重映射到新编号;丢弃(或记录)指向被折叠事件的引用。dsh-compaction中已有平衡检查——将其作为写入日志的硬性不变量强制执行,而不只是内存检查),和/或让消息重建跳过未配对的tool-result块,而不是发出非法线上序列。scanLog/校验,若日志不可加载则回滚或标记会话,而不是提交一份无法加载的日志。dsh session repair),对现有日志执行重编号 + 重映射——目前受影响会话的用户没有任何恢复手段。临时方案(我们的恢复方法,已验证有效)
一个独立修复脚本,对每个会话日志:
0xFD2FB528)定位每个 zstd 帧并逐帧解压(zstdDecompressSync),因为日志是拼接帧容器(原文件 53,327 帧;内置流式解压器只解码第一帧);seq(以及打包 chunk 行的seq0)重写为全局连续的 0..N 序列,逐字保留行内容;sourceEventSeqs条目和surfaceOp.replace的 start/end;丢弃悬空引用(当非assistant/message事件上的sourceEventSeqs字段清空时整个删除该字段);[frame0 = header][frame1 = all events]重新编码,带ZSTD_c_checksumFlag(与持久化层使用的参数相同);结果(两个会话现已正常打开并持续可用):
所有中间状态的备份均保留在磁盘上;没有任何消息内容丢失(只丢失了压缩本身折叠掉的事件)。
附录 —— 观察到的确切错误串
中文摘要
问题:DeepSeek Harness 0.1.0-rc.6 中,对 ~80 万 token 的大会话执行强制上下文压缩(
/compact)后,重启 host 再打开该会话会报seq gap in committed region等错误,历史永远无法加载。根因(代码级证据):压缩事务(
@deepseek-ai/dsh-compaction)用 surface replace 把一段历史折叠成一个摘要节点,但没有重排剩余事件的 seq 编号,也没有重映射sourceEventSeqs/surfaceOp.replace引用。而加载器(dsh-session-persistence-jsonl的SessionLogScanner)要求事件 seq 必须从 0 全局连续,校验引用必须指向更早的事件——因此 DSH 自己写出的压缩日志,自己的加载器拒绝读取。三个症状同一根源:
seq gap(seq 空洞);sourceEventSeqs must reference earlier events(引用未重映射,1 个引用指向被折叠删除的事件);tool/result,消息重建时产生未配对的tool_calls)。证据:未被任何工具修改过的原始备份本身就含 1,415 个 seq 空洞;67.5 万个引用中有 67.5 万个需要重映射。
建议修复:压缩后重编号 + 重映射引用 + 保证 tool 配对边界 + 写入时校验;并提供官方修复命令。
临时方案(已验证):解压全部 zstd 帧 → 重编号 seq → 重映射所有引用 → 按原 checksum 参数重写 → 磁盘回读验证。两个会话(695,462 / 1,594,969 事件)均恢复且持续正常使用,消息内容零丢失。
与已有讨论的关联(补充)
GitHub Discussions 中已有两帖报告症状相似但根因不同的 bug:
seq 147513–147516 重复→ 加载失败seq 38422–38425逐字节重复两次二者的根因是跨进程并发写入(
appendLines使用无锁的open(path, 'a'),O_APPEND只保证单次 write 不撕裂,不保证 seq 不重叠;PersistenceCoordinator的串行化只覆盖进程内)。本报告与之不同:我们的会话全程只有单个进程写入;损坏形态是 seq 空洞(事件被压缩折叠删除后未重编号)而非重复,且伴随
sourceEventSeqs悬空引用与孤儿tool/result。这是/compact压缩路径自身的 bug,与并发写入无关。两者都值得维护者修复,但修复点不同(压缩事务 vs 跨进程锁)。All reactions