Разбор сбоя цепочки публикации через браузер 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 ... - для тех, кто хочет превратить «временное тушение пожара» в «стабильное переиспользуемое решение»
I. Симптомы и влияние на бизнес
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) не может выполняться непрерывно
Такой сбой легко ошибочно принять за «проблему прав аккаунта» или «слишком длинный контекст», но этот случай доказал, что причина не в этом.
II. Таймлайн (ключевые точки)
- около 2026-02-23 12:44 (CST): локальная установка/обновление OpenClaw до
2026.2.22-2 - после этого массово пошли ошибки browser:
fields are required - при дальнейшем продвижении появился устаревший ref (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) не исправляет напрямую эти две базовые ошибки.
III. Разбор первопричин
Причина 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
Это очень часто встречается в динамическом UI, например в поле ввода выбора узла на V2EX.
Причина C: No tool call found ... — рассинхронизация состояния вызовов сессии
Эта ошибка относится к несоответствию цепочки session/tool-call (call_id не совпадает), а не к падению DOM-операций браузера как таковых.
IV. Цели и стратегия исправления
Цель — не «точечный патч», а превращение цепочки публикации в «совместимость + самоисцеление + наблюдаемость»:
- поддержать старую форму запросов и избежать
fields are required - автоматическое восстановление при stale ref, без требования вручную делать snapshot каждый раз
- даже при втором провале — иметь страховку, чтобы процесс не падал прямо на критической точке записи
V. Фактические изменения (runtime dist)
Примечание: после сборки OpenClaw существует несколько hash-артефактов; нужно синхронно закрыть ключевые ветки, чтобы в рантайме не попался непропатченный bundle.
1) Слой совместимости fill
В case \"fill\" внутри /act добавлен совместимый маппинг:
- если
fieldsотсутствует, автоматически преобразовать{ref,text/value}вfields:[{ref,type:\"text\",value}] - и задать для
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
VI. Шаги проверки (воспроизводимо)
- Перезапустить gateway и убедиться, что новый процесс применился (PID изменился)
- Запустить тот же путь автопубликации
- Проверить, появляются ли в логах:
fields are requiredElement \"eXX\" not found or not visible
- Проверить результат в бизнесе: пост действительно создан успешно
Результат в этот раз:
- проблемный путь восстановлен, публикация успешна.
VII. Почему «два дня назад было нормально, а сейчас пришлось менять так много»?
Вывод однозначный:
-
Изменение версии усилило различия протокола
- текущая версия локально:
[email protected] - в этой версии вход
fillболее строго требует формуfields[]
- текущая версия локально:
-
Одну и ту же проблему нужно закрыть в нескольких dist-точках входа
- выглядит как «правили много файлов», но по сути это «одна и та же логика повторяется в нескольких собранных артефактах»
-
Дрейф ref на динамических страницах — естественный риск
- раньше не столкнулись — не значит, что его не было
- как только перерисовки стали происходить чаще, проблема начинает массово проявляться
Поэтому это не «чисто конфигурационная проблема», а одновременно разрыв в «совместимости протокола + устойчивости к динамическому UI».
VIII. Рекомендации дальше (против регрессий)
1) Рекомендации на уровне протокола
- в протоколе browser action явно закрепить canonical schema для
fill - для старой схемы держать окно обратной совместимости и в логах подсказывать миграцию
2) Рекомендации на уровне исполнения
- для
type/fill/clickдобавить настраиваемый переключатель автоматического snapshot перед действием (режим повышенной стабильности) - для веток retry по stale-ref писать структурированные логи (чтобы понимать, сработала ли страховка)
3) Рекомендации по эксплуатации (ops)
- после каждого обновления прогонять минимальный E2E: open → snapshot → fill → submit
- завести алерты на ключевые ошибки (
fields are required、Unknown ref)
4) Рекомендации на уровне сессий
No tool call found ...разбирать отдельно (целостность сессии/консистентность реплея), не смешивать с browser DOM-проблемами
IX. Чеклист диагностики (для себя в будущем)
Если встретили похожее, идти в таком порядке:
- сначала разделить тип ошибки (протокол/DOM/сессия)
- посмотреть таймстемпы свежих логов, чтобы не перепутать со старыми ошибками
- проверить, совпадает ли форма payload запроса с текущей версией
- добавить для stale ref «повтор со snapshot + страховка через activeElement»
- перезапустить и убедиться, что новый процесс загрузил новый код
- перепроверить тем же путём и подтвердить успех в бизнесе, а не только исчезновение ошибки
X. Заключение
Это исправление — не «спрятать ошибку», а довести цепочку публикации до трёх уровней устойчивости:
- первый уровень: совместимость протокола (защита от несовпадения структуры)
- второй уровень: автоматическое восстановление (повтор после snapshot)
- третий уровень: страховка ввода (запись через activeElement)
Итоговая цель одна: сделать автоматизацию устойчивой, воспроизводимой и поддерживаемой на реальных динамических веб-страницах.
Если вы тоже делаете браузерную автоматизацию в OpenClaw, стоит сразу закрепить эти три слоя как шаблон по умолчанию.