From ff1a63d1045fe4ca43e296e2b6d4018891be74d1 Mon Sep 17 00:00:00 2001 From: cabana8471 Date: Tue, 27 Jan 2026 06:32:07 +0100 Subject: [PATCH] fix: address CodeRabbit review feedback - Fix comment: "exponential" -> "linear" for error backoff (30 * retries) - Fix rate limit counter reset: only reset when no rate limit signal detected - Apply exponential backoff to rate limit in response text (not just exceptions) - Use explicit `is not None` check for retry_seconds to handle Retry-After: 0 Co-Authored-By: Claude Opus 4.5 --- agent.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/agent.py b/agent.py index 4ccbaa0..3b1bf63 100644 --- a/agent.py +++ b/agent.py @@ -166,7 +166,7 @@ async def run_agent_session( if is_rate_limit_error(error_str): # Try to extract retry-after time from error retry_seconds = parse_retry_after(error_str) - if retry_seconds: + if retry_seconds is not None: return "rate_limit", str(retry_seconds) else: return "rate_limit", "unknown" @@ -316,9 +316,9 @@ async def run_autonomous_agent( # Handle status if status == "continue": - # Reset retry counters on success - rate_limit_retries = 0 + # Reset error retries on success; rate-limit retries reset only if no signal error_retries = 0 + reset_rate_limit_retries = True delay_seconds = AUTO_CONTINUE_DELAY_SECONDS target_time_str = None @@ -327,8 +327,18 @@ async def run_autonomous_agent( response_lower = response.lower() if any(pattern in response_lower for pattern in RATE_LIMIT_PATTERNS): print("Claude Agent SDK indicated rate limit reached.") + reset_rate_limit_retries = False - # Try to parse reset time from response + # Try to extract retry-after from response text first + retry_seconds = parse_retry_after(response) + if retry_seconds is not None: + delay_seconds = retry_seconds + else: + # Use exponential backoff when retry-after unknown + delay_seconds = min(60 * (2 ** rate_limit_retries), 3600) + rate_limit_retries += 1 + + # Try to parse reset time from response (more specific format) match = re.search( r"(?i)\bresets(?:\s+at)?\s+(\d+)(?::(\d+))?\s*(am|pm)\s*\(([^)]+)\)", response, @@ -396,6 +406,10 @@ async def run_autonomous_agent( print(f"\nSingle-feature mode: Feature #{feature_id} session complete.") break + # Reset rate limit retries only if no rate limit signal was detected + if reset_rate_limit_retries: + rate_limit_retries = 0 + await asyncio.sleep(delay_seconds) elif status == "rate_limit": @@ -412,7 +426,7 @@ async def run_autonomous_agent( await asyncio.sleep(delay_seconds) elif status == "error": - # Non-rate-limit errors: shorter backoff but still exponential + # Non-rate-limit errors: linear backoff capped at 5 minutes error_retries += 1 delay_seconds = min(30 * error_retries, 300) # Max 5 minutes print("\nSession encountered an error")