You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by ak...@apache.org on 2026/07/13 23:45:50 UTC
(tooling-gofannon) branch main updated: llm_service: retry on provider overload (HTTP 529 overloaded_error)
This is an automated email from the ASF dual-hosted git repository.
andrewmusselman pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-gofannon.git
The following commit(s) were added to refs/heads/main by this push:
new b7ef744 llm_service: retry on provider overload (HTTP 529 overloaded_error)
b7ef744 is described below
commit b7ef744bf45f206ecdb925882b7032112a3f96e2
Author: Andrew Musselman <ak...@apache.org>
AuthorDate: Mon Jul 13 16:45:38 2026 -0700
llm_service: retry on provider overload (HTTP 529 overloaded_error)
---
.../api/user-service/services/llm_service.py | 42 +++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/webapp/packages/api/user-service/services/llm_service.py b/webapp/packages/api/user-service/services/llm_service.py
index dd3b40c..ece5383 100644
--- a/webapp/packages/api/user-service/services/llm_service.py
+++ b/webapp/packages/api/user-service/services/llm_service.py
@@ -39,6 +39,16 @@ MAX_TIMEOUT_RETRIES = int(os.getenv("LLM_TIMEOUT_RETRIES", "0"))
# ride through most quota refills.
MAX_RATE_LIMIT_RETRIES = int(os.getenv("LLM_RATE_LIMIT_RETRIES", "5"))
+# Max retries on provider overload (HTTP 529 overloaded_error). Distinct from
+# rate-limit: 529 is the provider saying "too busy right now", not a quota
+# bucket. litellm surfaces it as APIConnectionError (or a generic exception
+# with "overloaded" in the body), NOT as RateLimitError, so it would otherwise
+# fall through to the terminal branch and permanently fail the call. It is the
+# MOST retryable error there is; reuse the same exponential-backoff-with-jitter
+# as rate-limit. Especially important on newer or capacity-limited endpoints
+# under stacked concurrency, where transient overloads are more likely.
+MAX_OVERLOADED_RETRIES = int(os.getenv("LLM_OVERLOADED_RETRIES", "5"))
+
# Cap on individual backoff sleep, regardless of attempt number.
RATE_LIMIT_BACKOFF_CAP_SECONDS = int(os.getenv("LLM_RATE_LIMIT_BACKOFF_CAP", "60"))
@@ -527,9 +537,10 @@ async def call_llm(
# Other exceptions fall through to the error-classifier branch
# below (auth, context window, generic). Those are not retryable.
last_exception = None
- max_attempts = 1 + MAX_TIMEOUT_RETRIES + MAX_RATE_LIMIT_RETRIES
+ max_attempts = 1 + MAX_TIMEOUT_RETRIES + MAX_RATE_LIMIT_RETRIES + MAX_OVERLOADED_RETRIES
timeout_attempts_used = 0
rate_limit_attempts_used = 0
+ overloaded_attempts_used = 0
for attempt in range(max_attempts):
try:
@@ -589,6 +600,35 @@ async def call_llm(
)
raise
except Exception as e:
+ # Provider overload (HTTP 529 / overloaded_error). litellm
+ # surfaces this as APIConnectionError or a generic exception,
+ # NOT RateLimitError, so it is caught here and routed through
+ # the same exponential-backoff-with-jitter as rate-limit before
+ # falling through to the terminal error classifier. 529 means
+ # "provider busy, retry" and is highly likely to succeed on a
+ # backed-off retry.
+ _err_str = str(e).lower()
+ _is_overloaded = (
+ "overloaded" in _err_str
+ or "529" in _err_str
+ or "\"type\":\"overloaded_error\"" in _err_str
+ )
+ if _is_overloaded and overloaded_attempts_used < MAX_OVERLOADED_RETRIES:
+ last_exception = e
+ backoff = min(
+ RATE_LIMIT_BACKOFF_CAP_SECONDS,
+ (2 ** overloaded_attempts_used)
+ + random.uniform(0, overloaded_attempts_used + 1),
+ )
+ overloaded_attempts_used += 1
+ print(
+ f" Overloaded (529) on attempt {attempt + 1} "
+ f"for {model_string}; sleeping {backoff:.1f}s "
+ f"(retry {overloaded_attempts_used}/{MAX_OVERLOADED_RETRIES})",
+ flush=True,
+ )
+ await asyncio.sleep(backoff)
+ continue
observability = get_observability_service()
# Check for authentication errors and provide user-friendly message
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org