« Rétrospective de la panne de la chaîne de publication via navigateur d’OpenClaw : de “fields are required” à la correction système de “stale ref” »

Rétrospective d’un incident sur la chaîne de publication via le navigateur OpenClaw : de fields are required à la réparation système d’un stale ref

Cet article est une rétrospective complète d’un dépannage réel en production. L’objectif est d’expliquer clairement « pourquoi ça a cassé, pourquoi ça marchait avant, et qu’est-ce qui a été modifié cette fois », et de fournir des étapes de correction reproductibles.

Public concerné :

  • Ceux qui utilisent l’outil browser dans OpenClaw pour automatiser la publication / le remplissage de formulaires
  • Ceux qui ont déjà rencontré fields are required, Element \"eXX\" not found or not visible, No tool call found for function call output ...
  • Ceux qui veulent faire évoluer un “correctif d’urgence” en “solution stable et réutilisable”

I. Symptômes et impact métier

1) Principales erreurs

Lors de cet incident, trois erreurs typiques sont apparues :

  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) Comportement côté métier

  • La page s’ouvre
  • Le DOM peut être snapshot
  • Dès qu’on arrive à act/fill ou à une action de saisie sur un nœud, le service de contrôle browser renvoie une erreur et s’interrompt
  • La chaîne de publication multi-sites (Linux.do / V2EX / 2libra / iSharkFly) ne peut plus s’exécuter en continu

Ce type de panne est facile à confondre avec un “problème de permissions du compte” ou un “contexte trop long”, mais cette fois-ci a prouvé que ce n’était pas le cas.


II. Chronologie (jalons clés)

  • Vers 2026-02-23 12:44 (CST) : installation/mise à jour locale d’OpenClaw vers 2026.2.22-2
  • Ensuite, de nombreuses erreurs browser : fields are required
  • En poussant plus loin : apparition d’un stale ref : Element \"e92\" not found or not visible
  • Dans l’historique des sessions : No tool call found for function call output ...
  • Finalement : reprise de la publication réussie après compatibilité de protocole + contournement stale ref

Remarque :

  • No tool call found ... et fields are required ne sont pas le même problème.
  • Réduire la fenêtre de contexte (par ex. 1048576 → 200000) ne corrige pas directement ces deux erreurs de bas niveau.

III. Décomposition des causes racines

Cause racine A : incompatibilité du protocole fill (déclencheur initial)

La forme courante des requêtes d’action en amont est :

{
  "kind": "fill",
  "ref": "e57",
  "text": "..."
}

Alors que l’implémentation actuelle du routage tend à exiger :

{
  "kind": "fill",
  "fields": [
    { "ref": "e57", "type": "text", "value": "..." }
  ]
}

Quand fields n’est pas fourni et qu’il n’y a pas de mapping de compatibilité, cela déclenche directement fields are required.

Cause racine B : pages dynamiques entraînant une dérive de ref (stale ref)

Par exemple :

  • lors du snapshot, on obtient e92
  • après un repaint de composant / un changement de couche (overlay), e92 n’est plus valide
  • l’action type/fill suivante réutilise l’ancien ref et déclenche not found or not visible

C’est très fréquent dans des UI dynamiques comme le champ de sélection de nœud sur V2EX.

Cause racine C : No tool call found ... = désalignement de l’état d’appel session/tool-call

Cette erreur relève d’une incohérence de la chaîne session/tool-call (call_id ne correspond pas), et non d’un échec intrinsèque des opérations DOM du navigateur.


IV. Objectifs et stratégie de correction

L’objectif n’est pas un “patch ponctuel”, mais de rendre la chaîne de publication “compatible + auto-guérissante + observable” :

  1. Compatibilité avec les anciennes formes de requêtes, pour éviter fields are required
  2. Récupération automatique des stale ref, sans exiger un resnapshot manuel à chaque fois
  3. Un contournement même en cas de second échec, afin que le flux ne s’effondre pas au point d’écriture critique

V. Modifications réelles (dist à l’exécution)

Remarque : après le packaging, OpenClaw génère plusieurs artefacts hashés ; il faut combler les branches clés de manière synchronisée pour éviter qu’au runtime un bundle non corrigé soit utilisé.

1) Couche de compatibilité fill

Dans le case \"fill\" de /act, ajout d’un mapping de compatibilité :

  • lorsque fields est absent, conversion automatique de {ref,text/value} vers fields:[{ref,type:\"text\",value}]
  • et fourniture d’une valeur par défaut text pour le type du champ

2) Retry automatique sur stale ref

Dans case \"type\" et case \"fill\" :

  • capture de Unknown ref / not found or not visible
  • exécution automatique d’un snapshotRoleViaPlaywright({ refsMode: \"aria\" })
  • puis retry de l’action initiale

3) Deuxième niveau de contournement (amélioration clé de cette fois)

Si “resnapshot + retry” échoue encore à cause d’un stale ref :

  • pour type : tentative d’écriture directe dans document.activeElement et dispatch de input/change
  • pour fill : dans le cas d’un seul champ texte, même contournement via activeElement

Cela couvre le scénario fréquent “le focus est toujours sur l’input cible, mais l’ancien ref est devenu invalide”.

4) Fichiers concernés

  • /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. Étapes de validation (reproductibles)

  1. Redémarrer le gateway et confirmer que le nouveau processus est actif (PID changé)
  2. Déclencher le même flux d’auto-publication sur le même chemin
  3. Observer si les logs contiennent encore :
    • fields are required
    • Element \"eXX\" not found or not visible
  4. Vérifier le résultat métier : le post a-t-il été réellement créé avec succès ?

Résultat cette fois :

  • Le chemin en panne a été rétabli, publication réussie.

VII. Pourquoi “c’était OK avant-hier, et cette fois il faut autant modifier” ?

La conclusion est très claire :

  1. Le changement de version a amplifié les différences de protocole

    • Version locale actuelle : [email protected]
    • Dans cette version, le point d’entrée fill est plus strict sur la forme fields[]
  2. Le même problème doit être comblé dans plusieurs entrées dist

    • Ça donne l’impression de “modifier beaucoup de fichiers”, mais en réalité “la même logique existe en doublon dans plusieurs artefacts packagés”
  3. La dérive de ref sur pages dynamiques est un risque naturel

    • Ne pas être tombé dessus avant ne veut pas dire que ça n’existait pas
    • Dès que les repaints deviennent plus fréquents, ça explose en série

Donc cette fois, ce n’est pas un “pur problème de configuration”, mais bien un manque simultané en “compatibilité de protocole + stabilité UI dynamique”.


VIII. Recommandations (prévention de régression)

1) Recommandations côté protocole

  • Définir explicitement le schéma canonical de fill dans le protocole des actions browser
  • Conserver une fenêtre de rétrocompatibilité pour l’ancien schéma, et logger une indication de migration

2) Recommandations côté exécution

  • Ajouter un interrupteur configurable pour un snapshot automatique en amont de type/fill/click (mode haute stabilité)
  • Ajouter des logs structurés sur la branche de retry stale-ref (pour confirmer si le contournement a été déclenché)

3) Recommandations côté exploitation

  • Après chaque upgrade, exécuter un E2E minimal : open → snapshot → fill → submit
  • Brancher des alertes sur les erreurs clés (fields are required, Unknown ref)

4) Recommandations côté session

  • Traiter No tool call found ... séparément (intégrité de session / cohérence de relecture), sans le mélanger aux problèmes DOM du navigateur

IX. Checklist de dépannage (pour moi-même dans le futur)

En cas de problème similaire, suivre cet ordre :

  1. D’abord séparer les types d’erreurs (protocole / DOM / session)
  2. Vérifier le timestamp des derniers logs pour éviter de mal diagnostiquer sur une ancienne erreur
  3. Valider que la forme du payload de requête correspond à la version actuelle
  4. Ajouter “retry avec snapshot + contournement activeElement” pour les stale ref
  5. Redémarrer et confirmer que le nouveau processus a chargé le nouveau code
  6. Re-tester sur le même chemin, confirmer le succès métier et pas seulement la disparition de l’erreur

X. Conclusion

Cette correction ne consiste pas à “cacher l’erreur”, mais à compléter la chaîne de publication avec trois niveaux de résilience :

  • Niveau 1 : compatibilité de protocole (éviter l’inadéquation de structure)
  • Niveau 2 : récupération automatique (retry après snapshot)
  • Niveau 3 : contournement de saisie (écriture via activeElement)

L’objectif final est unique : permettre à l’automatisation de rester durable, reproductible et maintenable sur de vraies pages web dynamiques.

Si vous faites aussi de l’automatisation navigateur avec OpenClaw, je recommande de figer directement ces trois niveaux de stratégie comme modèle par défaut.