From b892ee2bcf1b65f3010c7229f4d61e574ada54ad Mon Sep 17 00:00:00 2001 From: xxxigm Date: Tue, 16 Jun 2026 21:20:14 +0700 Subject: [PATCH] fix(agent): summarize non-retryable API errors so raw HTML never leaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a non-retryable client error aborts the turn (e.g. a Codex/Cloudflare HTTP 403 "managed challenge" page), the conversation loop returned the failure dict with `error: str(api_error)` — the entire ~60KB HTML page. Downstream consumers deliver that field verbatim: a cron job dumped a Cloudflare challenge page to Discord, where it was split into ~31 messages. The sibling "max retries exhausted" path already collapses such bodies via `_summarize_api_error` (which extracts the / status from HTML error pages). This makes the non-retryable path consistent: compute the summary once and use it for both the status emit and the returned `error`. --- agent/conversation_loop.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/agent/conversation_loop.py b/agent/conversation_loop.py index ef69ac68329..163a508a8cd 100644 --- a/agent/conversation_loop.py +++ b/agent/conversation_loop.py @@ -3197,15 +3197,22 @@ def run_conversation( # Terminal — flush buffered context so the user sees # what was tried before the abort. agent._flush_status_buffer() + # Summarize once: Cloudflare/proxy HTML challenge pages and + # other raw provider bodies must be collapsed to a short + # one-liner here, otherwise the full page leaks into the + # returned ``error`` field and downstream consumers deliver + # it verbatim (e.g. a cron failure notification dumped a + # ~60KB Cloudflare challenge page as 31 Discord messages). + _nonretryable_summary = agent._summarize_api_error(api_error) if classified.reason == FailoverReason.content_policy_blocked: agent._emit_status( f"❌ Provider safety filter blocked this request: " - f"{agent._summarize_api_error(api_error)}" + f"{_nonretryable_summary}" ) else: agent._emit_status( f"❌ Non-retryable error (HTTP {status_code}): " - f"{agent._summarize_api_error(api_error)}" + f"{_nonretryable_summary}" ) agent._vprint(f"{agent.log_prefix}❌ Non-retryable client error (HTTP {status_code}). Aborting.", force=True) agent._vprint(f"{agent.log_prefix} 🔌 Provider: {_provider} Model: {_model}", force=True) @@ -3309,7 +3316,7 @@ def run_conversation( "api_calls": api_call_count, "completed": False, "failed": True, - "error": str(api_error), + "error": _nonretryable_summary, } if retry_count >= max_retries: