ACP 一次性任务明明已写入 .jsonl,但 sessions_history 可能返回空:怎么判断、怎么止损
OpenClaw News 编辑部
OpenClaw 的一个新 bug 报告指出:在某些 ACP one‑shot(sessions_spawn(runtime="acp", mode="run")) 场景里,任务已经成功完成、并且本地 transcript(.jsonl)里已经有完整的用户输入与助手输出,但你用 sessions_history(以及 sessions_list(...messageLimit=...))去“回看”时,却可能看到一个 空会话。
来源 Issue: openclaw/openclaw#51328
这类问题的危险点在于:它会把“已经跑完的任务”伪装成“没跑/卡住/没产出”,从而导致误判、重复执行、甚至重复产生副作用。
现象长什么样
报告环境大意(OpenClaw 2026.3.13、macOS、ACP backend 为 acpx,Claude ACP one‑shot):
sessions_history(sessionKey, includeTools=true)返回messages: []sessions_list(limit=..., messageLimit=2)能看到 session,但最近消息为空- 但磁盘上
~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl里已经有:- 完整的用户 prompt
- 完整的助手 completion
- 正常的 DONE 结果
关键点:这不是 ACP 没执行成功。更像是“会话 API 的可见性/投影层”暂时没跟上。
可能原因(不需要猜对才能处理)
OpenClaw 的 ACP 运行通常有两层:
- ACP backend 写入原始 transcript(
.jsonl) - 会话 API(
sessions_history/sessions_list)展示的是另一份“被加载/被索引/被投影”的结果
如果两层在短时间内不同步,就会出现“磁盘有内容、API 看起来为空”。可能触发因素包括:
- 异步摄取/索引延迟(写入先完成,但索引更新滞后)
- watcher/polling 触发慢
- 写入关闭/锁竞争导致的短暂读空窗口
你不必在现场定位根因,先按下面的步骤确认与止损更重要。
快速分流:这是“空 history”,还是任务其实根本没执行
现场最容易犯的错,是把所有“API 里没消息”都当成同一种故障。
更实用的判断顺序是:
- 磁盘 transcript 已完整写入,API 暂时为空
- 这更像本文讨论的投影/索引延迟,而不是真没执行。
- 磁盘 transcript 也没有用户输入和助手输出
- 这时就不能把问题直接归因到
sessions_history,要改查 ACP run 是否根本没启动或提前失败。
- 这时就不能把问题直接归因到
sessions_list能看到 session,但 recent messages 为空- 更像可见性层滞后,而不是 session 本体不存在。
- 不只是 one-shot,连 thread/session 模式都持续丢消息
- 那就不该把范围只锁在 ACP one-shot,要扩大到更广泛的会话投影链路。
这个分流很关键,因为它决定你下一步该“避免重跑”,还是该先确认任务到底有没有真正执行。
实用排障与止损清单
1) 先把 .jsonl 当成“是否真的跑过”的地面真相
当你怀疑 ACP one‑shot 已经成功,但 sessions_history 却空时:
- 找到 transcript 文件:
~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl
- 确认里面确实包含:用户输入 + 助手输出(最好还能看到 DONE/完成标记)
只要 .jsonl 已经完整,不要立刻重跑。
重跑的风险包括:
- 重复 commit / 重复发布
- 重复发送消息
- 重复消耗模型额度
- 让问题变得更难复盘(两次 run 混在一起)
2) 延迟 15–60 秒再查一次 API
这个问题在报告里是“spawn 后不久检查时出现”,所以可以做一个最小成本的确认:
- 等 15–60 秒
- 再调用一次
sessions_history(..., includeTools=true)
如果稍后恢复正常,基本就能确定是“投影/索引延迟”而非真实丢失。
3) 如果持续为空:收集对维护者最有用的证据
不要只截一句“空了”。建议收集:
- 触发的
sessions_spawn关键参数(runtime、agentId、mode 等) - 你用的
sessionKey - transcript
.jsonl的前 50 行 + 后 50 行(注意脱敏) - 两个 API 输出:
sessions_history(sessionKey, includeTools=true)sessions_list(limit=..., messageLimit=2)
这能帮助判断:是索引没更新、读源不一致、还是在某个解析边界上失败。
4) 临时运维策略:避免“看到空历史就自动重试”
在问题修复前,如果你有自动化编排逻辑(或者团队 runbook):
- 不要把
sessions_history的空列表直接等价于“没跑/失败” - 增加一个短延迟 + 磁盘 transcript 兜底检查
- 对任何会产生副作用的动作,尽量设计成幂等(idempotent)
为什么这个点对“生产化使用”很关键
ACP one‑shot 常用于“做完就退出”的任务(写代码、修复、生成报告、跑测试等)。如果会话 API 错把成功的 run 显示为空,会导致:
- 误触发重跑与重复副作用
- 让运维以为 ACP 不稳定、误判为启动/路由失败
- 增加排障成本与模型开销
常见上线判断问题
什么时候该停止怀疑 ACP agent 本身,改为优先怀疑会话投影延迟
如果磁盘里的 transcript .jsonl 已经有完整 prompt、完整 completion,且还能看到正常 DONE 标记,但 sessions_history 依然返回 messages: [],这时就不该继续把问题归因于“agent 没跑起来”。更安全的判断是:磁盘真实状态已经完成,但 session API 的投影/索引层还没跟上。
在这个 bug 还没修好前,怎样最安全地避免重复副作用
对任何准备重试的动作,先加一个两步保护:短暂等待,再检查磁盘 transcript 是否已经显示任务完成。如果已经完成,就不要自动重跑会产生副作用的动作,比如 commit、deploy 或对外发消息,除非有人明确判断第一次结果必须重做。
如果你是从 troubleshooting 入口进来,先分清 transcript 证据和 API 投影
这篇现在和 troubleshooting 总表、setup 流量出现在同一个 GA4 窗口里,说明读者大概率不是随便看一篇 bug 记录,而是在判断 ACP run 到底是丢了、延迟了,还是只是 session API 视图过旧。
升级事故前,先按三类分流:
- 没有 transcript 文件:先查启动、权限、sandbox 路径和 runtime 初始化,这还不是 empty history 投影问题。
- transcript 存在,但
sessions_history为空:保留.jsonl、session id、runtime 和时间戳,先按 transcript 继续交接,把 API 视图当作 stale。 - 多次检查后 transcript 和 API 仍不一致:把问题带回 troubleshooting 总表,对照 session timeout 或 Gateway 状态症状,再决定是否重启生产自动化。
这能让从泛排障搜索进来的高意图用户,快速完成“run 真丢了 vs API 视图过旧”的判断。
相关阅读
- Kimi-Claw 某些会话会在用户提问后立刻被强制重置:怎么区分“被重建”和“只是看起来没历史”
- OpenClaw Agents 排障指南:任务卡住、工具不回、结果异常时先查什么
- OpenClaw Feishu 配置可能在官方插件加载前就被核心 Schema 拒掉
- OpenClaw Compaction 卡住恢复指南
常见判断问题 FAQ
如果 .jsonl 已经有完整 completion,但 sessions_history 还是空,第一反应应该是什么?
第一反应不该是立刻重跑,而应该先把它当成 会话投影延迟 来处理。
因为这类故障最危险的点就在于:
- 真正执行已经完成
- 本地 transcript 已经落盘
- 但 API 视图暂时没把结果显出来
这时马上重跑,最容易制造重复副作用。
什么时候更该怀疑“索引/可见性层没跟上”,而不是 ACP agent 本身没工作?
如果你同时看到:
sessions_list能看到 session.jsonl里已经有用户输入和助手输出- 只有
sessions_history或 recent messages 为空
那就更该优先怀疑可见性层或索引层,而不是 agent 根本没执行。
搜 sessions_history empty but jsonl exists 的人,页面最该先帮他分哪三个支路?
最该先分这三个:
- 磁盘 transcript 是否已经完整写入
- 这是短时间检查时为空,还是长时间后仍持续为空
- 只有 one-shot 受影响,还是 thread/session 模式也一起异常
这三步最能帮助用户从“误以为任务没跑”收缩到“会话 API 投影延迟或可见性问题”。
如果你已经确认 transcript 在、API 视图却空,下一步先点哪两篇
如果你已经确认 .jsonl transcript 是完整的,但 sessions_history 或 sessions_list(...messageLimit=...) 仍然看起来像空会话,下一步最值得继续看的通常是这两篇:
- 先看 OpenClaw Agents 排障指南:任务卡住、工具不回、结果异常时先查什么,确认是不是还叠加了工具调用、权限或消息链路问题;
- 如果你的症状更像“session 还在,但重启或恢复后路径对不上”,再看 Gateway crash/restart 后 orphaned sessions 不会真正恢复,把会话投影延迟和恢复链路断开区分开。
这样做的意义,是把“API 看起来没历史”继续分流成更具体的消息可见性问题,而不是让用户停留在模糊的‘好像没跑’判断里。
相关阅读
交接给值班同事时,最少要留下哪 5 个证据
这类问题最怕只留一句“history 是空的”,因为下一班很容易误以为任务没跑。交接时至少留下:
sessionKey、agentId、runtime、mode,避免下一步查错会话;.jsonltranscript 的文件路径,以及开头和结尾各一小段脱敏内容;sessions_history(..., includeTools=true)的原始返回,保留空数组或缺字段细节;sessions_list(..., messageLimit=2)的返回,确认列表视图和 history 视图是否同时为空;- 是否已经执行过会产生副作用的动作,比如 commit、push、deploy、发消息或写外部 API。
有了这 5 个证据,后续判断就能从“任务是不是没跑”推进到“磁盘完成态、API 投影视图、列表摘要视图到底哪一层不同步”。这比直接重试更安全,也更适合生产化 runbook。
判定已恢复前,再做 4 个关闭检查
当 sessions_history 后来终于能看到消息时,也不要马上把事故关掉。先补 4 个关闭检查:
- 同一 sessionKey 复查:确认恢复的是原来的会话,不是后来重跑出来的新会话。
- list 与 history 对齐:同时对比
sessions_list(...messageLimit=2)和sessions_history(...includeTools=true),确认摘要视图与详情视图都能看到同一批消息。 - 工具消息可见性:如果该 run 调过工具,确认 tool call 与 tool result 没有只存在于
.jsonl,却在 API 里缺失。 - 副作用没有重复:核对 commit、push、deploy、外部消息或 API 写入次数,确认恢复过程中没有因为空 history 误触发第二次执行。
这 4 个关闭检查能把“页面终于不空了”升级成真正可交接的恢复结论,也能服务搜索 sessions_list messageLimit empty、ACP transcript exists history empty 的运维读者。
信任空 session API 响应前,先补这份证据清单
当 transcript 明明存在,但 session API 返回空 history 时,不要立刻把它当成真实丢失。先把 API 响应当作一个投影视图,按下面四项补证据:
- Transcript 路径与时间戳:记录本地 transcript 文件、最后写入时间,以及能证明任务确实执行过的 message id 或 turn。
- API 入口与调用上下文:说明空响应来自
sessions_history、dashboard 投影,还是其他 wrapper。 - Session 身份映射:对齐可见 session key、ACP session id、持久化 run id,避免把投影错配误判成 history 丢失。
- 新鲜度窗口:短暂等待后复查,再判断是 projection lag、查错 session,还是确实 ingestion 失败。
这样值班同事不会因为一个 API 视图暂时为空,就误删仍然有价值的 transcript。
搜索入口分流:transcript 有内容但 session API 空历史先看什么
如果你是从 “ACP transcript exists but history empty”、“session API returns empty history” 或 “OpenClaw 会话记录读不到” 搜到这里,先把问题拆成四层:
- 确认 transcript 与 session API 是否读同一个来源:本地 transcript 文件存在,不等于 API 查询的 session id、workspace 或 run scope 一致。
- 确认历史读取窗口:有些接口会按 active session、thread、agent runtime 或 resume id 过滤,导致文件有内容但 API 返回空数组。
- 确认写入和索引时序:crash、重启、后台子代理结束、flush 延迟,都可能让 transcript 先落盘而 session 索引尚未更新。
- 保留三段证据:transcript 路径、session API 请求参数、返回的空 history,比单独截图“没历史”更容易定位。
这类问题不要只在 UI 层刷新重试。最短路径是同时核对 transcript 文件、session 标识、API 过滤条件和索引刷新时序。
相关阅读
- OpenClaw Agents 排障指南:任务卡住、工具不回、结果异常时先查什么
- sessions_send 超时排障:子代理不返回时先看哪一层
- Gateway 崩溃重启后没有恢复孤儿 session 时,先查什么
- OpenClaw 记忆系统:上下文为什么会被记住或忘掉
什么时候空 history 应该阻断自动化
大多数 empty-history 事故应该让 runbook 降速,而不是让所有人停工。只有当缺失的 history 可能掩盖不可逆副作用时,才应该阻断自动化。让 cron、部署机器人或重试循环继续前,先用这条规则:
- 立刻阻断:上一次 run 可能已经 commit、push、deploy、发送外部消息、扣费,或写过第三方 API。
- 允许只读继续:下一步只是收集 transcript 路径、session id、日志或公网页面状态。
- 要求人工检查点:transcript 已证明任务完成,但 API 视图为空,而下一步会重复同一个副作用。
- 恢复自动化:只有当 transcript、
sessions_list、sessions_history对原始 run 达成一致,或重复副作用检查完成后再恢复。
这能回答高意图搜索里的 sessions_history empty retry or wait:涉及副作用先等证据,不要重复执行;只读诊断则继续推进。
来源
- Issue: openclaw/openclaw#51328
从搜索进来后的 3 分钟分流:run 丢失、API 过旧,还是查错 session
如果你是通过 sessions_history empty、jsonl exists 或 ACP transcript empty history 搜到这里,先不要重跑任务。用 3 分钟把风险分到正确层:
- 没有
.jsonl文件或文件为空:优先查 ACP 启动、工作目录、权限和 sandbox 挂载,这还不是 API 投影问题。 .jsonl已经有 prompt 与 completion,但 API 为空:把磁盘 transcript 当作临时事实源,暂停自动重试,先补 session id、时间戳和 API 原始响应。- list 能看到 session,history 看不到消息:优先怀疑 summary/history 投影不同步,而不是 agent 没执行。
- 不同 session key 查到不同结果:先对齐 visible session key、ACP session id 和持久化 run id,避免把身份映射错误误判成历史丢失。
这一步能把高意图排障流量从“看起来没跑”推进到“执行态、投影视图、身份映射哪一层不一致”,降低重复 commit、重复 deploy 或重复外部写入的概率。
