From 613321180ebc5ff9d490d696f807e7ead683a406 Mon Sep 17 00:00:00 2001 From: majdyz Date: Fri, 10 Apr 2026 12:00:08 +0000 Subject: [PATCH] fix(orchestrator): propagate InsufficientBalanceError past outer loop catch-all Follow-up to 6d2d476 addressing sentry's newly-posted review finding. Both `_execute_tools_agent_mode` and `_execute_tools_sdk_mode` had broad `except Exception` blocks at the top level of the tool-calling loop that would still swallow `InsufficientBalanceError` even after the inner tool executor carve-outs re-raised it: the error would escape the inner `_execute_single_tool_with_manager`, propagate up through `_agent_mode_tool_executor`, then get caught by the outer loop's catch-all and converted into a user-visible "error" yield. Add explicit `except InsufficientBalanceError: raise` carve-outs before each broad handler so billing failures propagate all the way out of the block's `run()` generator, reaching the executor's billing-leak handling (error recording on execution_stats, user notification, structured log). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../backend/backend/blocks/orchestrator.py | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/autogpt_platform/backend/backend/blocks/orchestrator.py b/autogpt_platform/backend/backend/blocks/orchestrator.py index 13591bed01..1f2861c87c 100644 --- a/autogpt_platform/backend/backend/blocks/orchestrator.py +++ b/autogpt_platform/backend/backend/blocks/orchestrator.py @@ -1407,9 +1407,17 @@ class OrchestratorBlock(Block): "arguments": tc.arguments, }, ) + except InsufficientBalanceError: + # Billing failures must propagate out of the block so the + # executor's billing-leak handling fires (error recording on + # execution_stats, user notification, structured logging). + # Do NOT downgrade to a user-visible "error" output — that + # would swallow the failure and leak the exact balance amount. + raise except Exception as e: - # Catch all errors (validation, network, API) so that the block - # surfaces them as user-visible output instead of crashing. + # Catch all OTHER errors (validation, network, API) so that + # the block surfaces them as user-visible output instead of + # crashing. yield "error", str(e) return @@ -1774,11 +1782,19 @@ class OrchestratorBlock(Block): await pending_task except (asyncio.CancelledError, StopAsyncIteration): pass + except InsufficientBalanceError: + # Billing failures must propagate so the executor's + # billing-leak handling fires (error recording, user + # notification, structured logging). Mirrors the carve-out + # in _execute_tools_agent_mode — do not downgrade to a + # user-visible error output. The `finally` block below + # still runs and records partial token usage. + raise except Exception as e: - # Surface SDK errors as user-visible output instead of crashing, - # consistent with _execute_tools_agent_mode error handling. - # Don't return yet — fall through to merge_stats below so - # partial token usage is always recorded. + # Surface OTHER SDK errors as user-visible output instead + # of crashing, consistent with _execute_tools_agent_mode + # error handling. Don't return yet — fall through to + # merge_stats below so partial token usage is always recorded. sdk_error = e finally: # Always record usage stats, even on error. The SDK may have