From c4d0c6c9b25874352df2bfc312c450203b583e61 Mon Sep 17 00:00:00 2001 From: Auto Date: Sun, 1 Feb 2026 10:39:07 +0200 Subject: [PATCH] fix: address rate limit detection false positives and reset-time cap - Narrow `\boverloaded\b` regex to require server/api/system context, preventing false positives when Claude discusses method/operator overloading in OOP code (C++, Java, C#, etc.) - Restore 24-hour cap for absolute reset-time delays instead of 1-hour clamp, avoiding unnecessary retry loops when rate limits reset hours in the future - Add test for Retry-After: 0 returning 0 (regression lock for the `is not None` fix) - Add false positive tests for "overloaded" in programming context Co-Authored-By: Claude Opus 4.5 --- agent.py | 2 +- rate_limit_utils.py | 2 +- test_rate_limit_utils.py | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/agent.py b/agent.py index 9bd2c3b..e64f38b 100644 --- a/agent.py +++ b/agent.py @@ -325,7 +325,7 @@ async def run_autonomous_agent( target += timedelta(days=1) delta = target - now - delay_seconds = clamp_retry_delay(int(delta.total_seconds())) + delay_seconds = min(max(int(delta.total_seconds()), 1), 24 * 60 * 60) target_time_str = target.strftime("%B %d, %Y at %I:%M %p %Z") except Exception as e: diff --git a/rate_limit_utils.py b/rate_limit_utils.py index 9bdbb22..9c06f68 100644 --- a/rate_limit_utils.py +++ b/rate_limit_utils.py @@ -18,7 +18,7 @@ RATE_LIMIT_REGEX_PATTERNS = [ r"\bstatus\s*429\b", # "status 429", "status429" r"\berror\s*429\b", # "error 429", "error429" r"\b429\s+too\s+many", # "429 too many" - r"\boverloaded\b", # "overloaded" + r"\b(?:server|api|system)\s+(?:is\s+)?overloaded\b", # "server is overloaded", "api overloaded" r"\bquota\s*exceeded\b", # "quota exceeded" ] diff --git a/test_rate_limit_utils.py b/test_rate_limit_utils.py index 55ecaa7..4c91ef2 100644 --- a/test_rate_limit_utils.py +++ b/test_rate_limit_utils.py @@ -43,6 +43,11 @@ class TestParseRetryAfter(unittest.TestCase): assert parse_retry_after("60 seconds left") == 60 assert parse_retry_after("120 seconds until reset") == 120 + def test_retry_after_zero(self): + """Test 'Retry-After: 0' returns 0 (not None).""" + assert parse_retry_after("Retry-After: 0") == 0 + assert parse_retry_after("retry after 0 seconds") == 0 + def test_no_match(self): """Test messages that don't contain retry-after info.""" assert parse_retry_after("no match here") is None @@ -141,6 +146,17 @@ class TestFalsePositives(unittest.TestCase): assert is_rate_limit_error("File size limit reached") is False assert is_rate_limit_error("Memory limit reached, consider optimization") is False + def test_overloaded_in_programming_context(self): + """Method/operator overloading discussions should not trigger.""" + assert is_rate_limit_error("I will create an overloaded constructor") is False + assert is_rate_limit_error("The + operator is overloaded") is False + assert is_rate_limit_error("Here is the overloaded version of the function") is False + assert is_rate_limit_error("The method is overloaded to accept different types") is False + # But actual API overload messages should still match + assert is_rate_limit_error("Server is overloaded") is True + assert is_rate_limit_error("API overloaded") is True + assert is_rate_limit_error("system is overloaded") is True + class TestBackoffFunctions(unittest.TestCase): """Test backoff calculation functions from rate_limit_utils."""