From e8a8a588c12063ca30f31c767a293a94fc206582 Mon Sep 17 00:00:00 2001 From: Dom Date: Tue, 31 Mar 2026 11:57:11 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20boucle=20de=20retry=20infinie=20?= =?UTF-8?q?=E2=80=94=20=5Fretry=5Fpending=20=C3=A9cras=C3=A9=20par=20l'env?= =?UTF-8?q?oi=20d'action?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug : _schedule_retry stockait retry_count=N dans _retry_pending, mais l'envoi de l'action (ligne 2173) écrasait avec retry_count=0. Résultat : le retry_count retombait toujours à 0, la condition retry_count < 3 restait vraie → boucle infinie de retries. Corrections : - Ne pas écraser _retry_pending si l'entrée existe déjà (set par _schedule_retry) - Guard de sécurité : extraire retry_count depuis les suffixes _retry de l'action_id Co-Authored-By: Claude Opus 4.6 (1M context) --- agent_v0/server_v1/api_stream.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/agent_v0/server_v1/api_stream.py b/agent_v0/server_v1/api_stream.py index ef41f5be5..9e86342f5 100644 --- a/agent_v0/server_v1/api_stream.py +++ b/agent_v0/server_v1/api_stream.py @@ -2168,8 +2168,9 @@ async def get_next_action(session_id: str, machine_id: str = "default"): # Else: queue a changé entre temps (race condition bénigne), on envoie quand même # Sauvegarder l'action envoyée pour le retry (si la vérification échoue) + # NE PAS écraser si _schedule_retry a déjà mis le bon retry_count action_id_sent = action.get("action_id", "") - if action_id_sent: + if action_id_sent and action_id_sent not in _retry_pending: _retry_pending[action_id_sent] = { "action": dict(action), "retry_count": 0, @@ -2228,6 +2229,18 @@ async def report_action_result(report: ReplayResultReport): retry_count = retry_info["retry_count"] if retry_info else 0 original_action = retry_info["action"] if retry_info else None + # Guard de sécurité : détecter le retry_count depuis l'action_id si non trouvé + # Évite la boucle infinie si _retry_pending est désynchronisé + if retry_count == 0 and "_retry" in action_id: + import re + retry_suffixes = re.findall(r"_retry\d+", action_id) + retry_count = max(retry_count, len(retry_suffixes)) + if retry_count > 0: + logger.warning( + f"retry_count corrigé par action_id : {retry_count} " + f"(action_id contient {len(retry_suffixes)} suffixes _retry)" + ) + # Mettre à jour le dernier screenshot reçu screenshot_after = report.screenshot_after or report.screenshot if screenshot_after: