From 5fc5ced9725a13227c5aa426739342fa1f8400ff Mon Sep 17 00:00:00 2001 From: Teknium Date: Thu, 9 Apr 2026 17:05:43 -0700 Subject: [PATCH] fix: add Alibaba/DashScope rate-limit pattern to error classifier Port from anomalyco/opencode#21355: Alibaba's DashScope API returns a unique throttling message ('Request rate increased too quickly...') that doesn't match standard rate-limit patterns ('rate limit', 'too many requests'). This caused Alibaba errors to fall through to the 'unknown' category rather than being properly classified as rate_limit with appropriate backoff/rotation. Add 'rate increased too quickly' to _RATE_LIMIT_PATTERNS and test with the exact error message observed from the Alibaba provider. --- agent/error_classifier.py | 1 + tests/agent/test_error_classifier.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/agent/error_classifier.py b/agent/error_classifier.py index 8c8bea82..dc5ae6b5 100644 --- a/agent/error_classifier.py +++ b/agent/error_classifier.py @@ -112,6 +112,7 @@ _RATE_LIMIT_PATTERNS = [ "try again in", "please retry after", "resource_exhausted", + "rate increased too quickly", # Alibaba/DashScope throttling ] # Usage-limit patterns that need disambiguation (could be billing OR rate_limit) diff --git a/tests/agent/test_error_classifier.py b/tests/agent/test_error_classifier.py index 7a46306f..b4bf7c5f 100644 --- a/tests/agent/test_error_classifier.py +++ b/tests/agent/test_error_classifier.py @@ -249,6 +249,22 @@ class TestClassifyApiError: assert result.reason == FailoverReason.rate_limit assert result.should_fallback is True + def test_alibaba_rate_increased_too_quickly(self): + """Alibaba/DashScope returns a unique throttling message. + + Port from anomalyco/opencode#21355. + """ + msg = ( + "Upstream error from Alibaba: Request rate increased too quickly. " + "To ensure system stability, please adjust your client logic to " + "scale requests more smoothly over time." + ) + e = MockAPIError(msg, status_code=400) + result = classify_api_error(e) + assert result.reason == FailoverReason.rate_limit + assert result.retryable is True + assert result.should_rotate_credential is True + # ── Server errors ── def test_500_server_error(self):