OpenClaw 浏览器发帖链路故障复盘:从 fields are required 到 stale ref 的系统级修复
这篇文章是一次真实线上排障的完整复盘,目标是把「为什么会炸、为什么之前没问题、这次到底改了什么」讲清楚,并给出可重复执行的修复步骤。
适用人群:
- 在 OpenClaw 中使用 browser 工具自动化发帖/填表的人
- 遇到过
fields are required、Element \"eXX\" not found or not visible、No tool call found for function call output ...的人 - 想把一次“临时救火”升级为“稳定可复用修复方案”的人
一、现象与业务影响
1) 主要报错
在本次故障中,出现了三类典型错误:
fields are requiredElement "e92" not found or not visible. Run a new snapshot to see current page elements.400 No tool call found for function call output with call_id ...
2) 业务表现
- 页面能打开
- DOM 能 snapshot
- 一到
act/fill或节点输入动作,browser 控制服务报错中断 - 导致跨站发帖流程(Linux.do / V2EX / 2libra / iSharkFly)无法连续执行
这类故障很容易误判成“账号权限问题”或“上下文太长”,但本次证明并非如此。
二、时间线(关键节点)
- 2026-02-23 12:44(CST)左右:本机 OpenClaw 安装/更新到
2026.2.22-2 - 之后出现大量 browser 报错:
fields are required - 进一步推进后,出现 stale ref:
Element "e92" not found or not visible - 历史会话中存在:
No tool call found for function call output ... - 最终通过协议兼容 + stale ref 兜底修复后恢复成功发帖
说明:
No tool call found ...与fields are required不是同一个问题。- 降低上下文窗口(比如 1048576 → 200000)不能直接修掉这两个底层错误。
三、根因拆解
根因 A:fill 协议不兼容(最先引爆)
上游动作请求常见形态是:
{
"kind": "fill",
"ref": "e57",
"text": "..."
}
而当前路由实现更偏向要求:
{
"kind": "fill",
"fields": [
{ "ref": "e57", "type": "text", "value": "..." }
]
}
当 fields 未提供且没有兼容映射时,就直接触发 fields are required。
根因 B:动态页面导致 ref 漂移(stale ref)
例如:
- snapshot 时拿到
e92 - 页面组件重绘/弹层切换后,
e92已失效 - 后续
type/fill仍拿旧 ref 执行,触发not found or not visible
这在 V2EX 节点选择输入框等动态 UI 非常常见。
根因 C:No tool call found ... 是会话调用状态错位
该错误属于 session/tool-call 链路不一致(call_id 对不上),不是浏览器 DOM 操作失败本身。
四、修复目标与策略
目标不是“单点补丁”,而是把发帖链路做成“兼容 + 自愈 + 可观测”:
- 兼容旧请求形态,避免
fields are required - stale ref 自动恢复,不要求人工每次重 snapshot
- 第二次失败仍有兜底,不让流程在关键写入点直接崩
五、实际改动(运行时 dist)
说明:OpenClaw 打包后存在多份 hash 产物,必须同步补齐关键分支,避免运行时命中未修补 bundle。
1) fill 兼容层
在 /act 的 case "fill" 中加入兼容映射:
- 当
fields缺失时,自动将{ref,text/value}转为fields:[{ref,type:"text",value}] - 并为 field 的
type提供默认值text
2) stale ref 自动重试
在 case "type" 与 case "fill" 中:
- 捕获
Unknown ref/not found or not visible - 自动执行一次
snapshotRoleViaPlaywright({ refsMode: "aria" }) - 然后重试原动作
3) 第二层兜底(本次关键增强)
如果“重 snapshot + 重试”后仍因 stale ref 失败:
- 对
type:尝试向document.activeElement直接写入值并派发input/change - 对
fill:在单字段文本场景下,同样走 activeElement 写入兜底
这能覆盖“焦点仍在目标输入框,但旧 ref 已失效”的高频场景。
4) 涉及文件
/opt/homebrew/lib/node_modules/openclaw/dist/routes-CmNAokG-.js/opt/homebrew/lib/node_modules/openclaw/dist/routes-FGJF5gtZ.js/opt/homebrew/lib/node_modules/openclaw/dist/pi-embedded-helpers-CNhhELVT.js/opt/homebrew/lib/node_modules/openclaw/dist/pi-embedded-helpers-DxTyisc4.js
六、验证步骤(可复现)
- 重启 gateway,确认新进程生效(PID 变化)
- 触发同一路径自动发帖流程
- 观察日志是否仍出现:
fields are requiredElement "eXX" not found or not visible
- 核验业务结果:发帖是否真实创建成功
本次结果:
- 故障路径已恢复,发帖成功。
七、为什么“前两天正常,这次要改这么多”?
结论很明确:
-
版本变化放大了协议差异
- 本机当前版本:
[email protected] - 该版本下
fill入口对fields[]形态更严格
- 本机当前版本:
-
同一问题需要在多个 dist 入口补齐
- 看起来“改很多文件”,本质是“同一逻辑在多个打包产物重复存在”
-
动态页面 ref 漂移是天然风险
- 之前没撞上,不代表不存在
- 一旦页面重绘更频繁,就会集中爆发
因此这次不是“纯配置问题”,而是“协议兼容 + 动态 UI 稳定性”同时缺口。
八、后续建议(防回归)
1) 协议层建议
- 在 browser action 协议中明确
fill的 canonical schema - 对旧 schema 保持向后兼容窗口,并在日志提示迁移
2) 执行层建议
- 对
type/fill/click增加可配置的自动 snapshot 前置开关(高稳态模式) - 对 stale-ref 重试分支打结构化日志(便于确认是否触发兜底)
3) 运维层建议
- 每次升级后跑一个最小 E2E:open → snapshot → fill → submit
- 将关键错误(
fields are required、Unknown ref)接入告警
4) 会话层建议
No tool call found ...独立排查(会话完整性/重放一致性),不要与 browser DOM 问题混排
九、排障清单(给未来的自己)
遇到类似问题,按这个顺序走:
- 先分离错误类型(协议/DOM/session)
- 查最新日志时间点,避免拿旧报错误判
- 验证请求 payload 形态是否与当前版本匹配
- 为 stale ref 增加“snapshot 重试 + activeElement 兜底”
- 重启并确认新进程加载了新代码
- 用同路径复测,确认业务成功而非仅错误消失
十、结语
这次修复不是“把报错藏起来”,而是把发帖链路补成了三层韧性:
- 第一层:协议兼容(防止结构不匹配)
- 第二层:自动恢复(snapshot 后重试)
- 第三层:输入兜底(activeElement 写入)
最终目标只有一个:让自动化流程在真实动态网页里可持续、可复现、可维护。
如果你也在做 OpenClaw 浏览器自动化,建议直接把这三层策略固化为默认模板。
附:上游 PR 跟进
本次修复已提交到 OpenClaw 官方仓库 PR(英文):
PR 关联问题:
- Fixes #15046
- Fixes #23552
- Related to #14503
- Related to #19758
说明:该 PR 主要覆盖 browser act/fill 兼容与 stale-ref 恢复策略;No tool call found ... 属于独立会话链路问题,已在 issue 中单独跟进。