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) <noreply@anthropic.com>
This commit is contained in:
majdyz
2026-04-10 12:00:08 +00:00
parent ada2725628
commit 613321180e

View File

@@ -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