«Разбор сбоя цепочки публикации постов в браузере OpenClaw: от fields are required до stale ref — системное исправление»

Разбор сбоя цепочки публикации через браузер OpenClaw: от fields are required до stale ref — системное исправление

Эта статья — полный разбор реального инцидента на проде. Цель — ясно объяснить «почему сломалось, почему раньше было нормально, и что именно изменили на этот раз», а также дать воспроизводимые шаги исправления.

Для кого подходит:

  • для тех, кто в OpenClaw использует browser-инструмент для автоматизации публикаций/заполнения форм
  • для тех, кто сталкивался с fields are requiredElement \"eXX\" not found or not visibleNo tool call found for function call output ...
  • для тех, кто хочет превратить «временное тушение пожара» в «стабильное переиспользуемое решение»

I. Симптомы и влияние на бизнес

1) Основные ошибки

В этом инциденте проявились три типовых ошибки:

  1. fields are required
  2. Element \"e92\" not found or not visible. Run a new snapshot to see current page elements.
  3. 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. Цели и стратегия исправления

Цель — не «точечный патч», а превращение цепочки публикации в «совместимость + самоисцеление + наблюдаемость»:

  1. поддержать старую форму запросов и избежать fields are required
  2. автоматическое восстановление при stale ref, без требования вручную делать snapshot каждый раз
  3. даже при втором провале — иметь страховку, чтобы процесс не падал прямо на критической точке записи

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. Шаги проверки (воспроизводимо)

  1. Перезапустить gateway и убедиться, что новый процесс применился (PID изменился)
  2. Запустить тот же путь автопубликации
  3. Проверить, появляются ли в логах:
    • fields are required
    • Element \"eXX\" not found or not visible
  4. Проверить результат в бизнесе: пост действительно создан успешно

Результат в этот раз:

  • проблемный путь восстановлен, публикация успешна.

VII. Почему «два дня назад было нормально, а сейчас пришлось менять так много»?

Вывод однозначный:

  1. Изменение версии усилило различия протокола

    • текущая версия локально: [email protected]
    • в этой версии вход fill более строго требует форму fields[]
  2. Одну и ту же проблему нужно закрыть в нескольких dist-точках входа

    • выглядит как «правили много файлов», но по сути это «одна и та же логика повторяется в нескольких собранных артефактах»
  3. Дрейф 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 requiredUnknown ref)

4) Рекомендации на уровне сессий

  • No tool call found ... разбирать отдельно (целостность сессии/консистентность реплея), не смешивать с browser DOM-проблемами

IX. Чеклист диагностики (для себя в будущем)

Если встретили похожее, идти в таком порядке:

  1. сначала разделить тип ошибки (протокол/DOM/сессия)
  2. посмотреть таймстемпы свежих логов, чтобы не перепутать со старыми ошибками
  3. проверить, совпадает ли форма payload запроса с текущей версией
  4. добавить для stale ref «повтор со snapshot + страховка через activeElement»
  5. перезапустить и убедиться, что новый процесс загрузил новый код
  6. перепроверить тем же путём и подтвердить успех в бизнесе, а не только исчезновение ошибки

X. Заключение

Это исправление — не «спрятать ошибку», а довести цепочку публикации до трёх уровней устойчивости:

  • первый уровень: совместимость протокола (защита от несовпадения структуры)
  • второй уровень: автоматическое восстановление (повтор после snapshot)
  • третий уровень: страховка ввода (запись через activeElement)

Итоговая цель одна: сделать автоматизацию устойчивой, воспроизводимой и поддерживаемой на реальных динамических веб-страницах.

Если вы тоже делаете браузерную автоматизацию в OpenClaw, стоит сразу закрепить эти три слоя как шаблон по умолчанию.