From 414db44c006c9448575da091fd1f3a36a7697a37 Mon Sep 17 00:00:00 2001 From: sususu Date: Tue, 30 Dec 2025 16:07:32 +0800 Subject: [PATCH] fix(antigravity): parse retry-after delay from 429 response body When receiving HTTP 429 (Too Many Requests) responses, parse the retry delay from the response body using parseRetryDelay and populate the statusErr.retryAfter field. This allows upstream callers to respect the server's requested retry timing. Applied to all error paths in Execute, executeClaudeNonStream, ExecuteStream, CountTokens, and refreshToken functions. --- .../runtime/executor/antigravity_executor.go | 72 ++++++++++++++++--- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/internal/runtime/executor/antigravity_executor.go b/internal/runtime/executor/antigravity_executor.go index 9ade4fbb..38dc0b84 100644 --- a/internal/runtime/executor/antigravity_executor.go +++ b/internal/runtime/executor/antigravity_executor.go @@ -155,7 +155,13 @@ func (e *AntigravityExecutor) Execute(ctx context.Context, auth *cliproxyauth.Au log.Debugf("antigravity executor: rate limited on base url %s, retrying with fallback base url: %s", baseURL, baseURLs[idx+1]) continue } - err = statusErr{code: httpResp.StatusCode, msg: string(bodyBytes)} + sErr := statusErr{code: httpResp.StatusCode, msg: string(bodyBytes)} + if httpResp.StatusCode == http.StatusTooManyRequests { + if retryAfter, parseErr := parseRetryDelay(bodyBytes); parseErr == nil && retryAfter != nil { + sErr.retryAfter = retryAfter + } + } + err = sErr return resp, err } @@ -169,7 +175,13 @@ func (e *AntigravityExecutor) Execute(ctx context.Context, auth *cliproxyauth.Au switch { case lastStatus != 0: - err = statusErr{code: lastStatus, msg: string(lastBody)} + sErr := statusErr{code: lastStatus, msg: string(lastBody)} + if lastStatus == http.StatusTooManyRequests { + if retryAfter, parseErr := parseRetryDelay(lastBody); parseErr == nil && retryAfter != nil { + sErr.retryAfter = retryAfter + } + } + err = sErr case lastErr != nil: err = lastErr default: @@ -259,7 +271,13 @@ func (e *AntigravityExecutor) executeClaudeNonStream(ctx context.Context, auth * log.Debugf("antigravity executor: rate limited on base url %s, retrying with fallback base url: %s", baseURL, baseURLs[idx+1]) continue } - err = statusErr{code: httpResp.StatusCode, msg: string(bodyBytes)} + sErr := statusErr{code: httpResp.StatusCode, msg: string(bodyBytes)} + if httpResp.StatusCode == http.StatusTooManyRequests { + if retryAfter, parseErr := parseRetryDelay(bodyBytes); parseErr == nil && retryAfter != nil { + sErr.retryAfter = retryAfter + } + } + err = sErr return resp, err } @@ -324,7 +342,13 @@ func (e *AntigravityExecutor) executeClaudeNonStream(ctx context.Context, auth * switch { case lastStatus != 0: - err = statusErr{code: lastStatus, msg: string(lastBody)} + sErr := statusErr{code: lastStatus, msg: string(lastBody)} + if lastStatus == http.StatusTooManyRequests { + if retryAfter, parseErr := parseRetryDelay(lastBody); parseErr == nil && retryAfter != nil { + sErr.retryAfter = retryAfter + } + } + err = sErr case lastErr != nil: err = lastErr default: @@ -599,7 +623,13 @@ func (e *AntigravityExecutor) ExecuteStream(ctx context.Context, auth *cliproxya log.Debugf("antigravity executor: rate limited on base url %s, retrying with fallback base url: %s", baseURL, baseURLs[idx+1]) continue } - err = statusErr{code: httpResp.StatusCode, msg: string(bodyBytes)} + sErr := statusErr{code: httpResp.StatusCode, msg: string(bodyBytes)} + if httpResp.StatusCode == http.StatusTooManyRequests { + if retryAfter, parseErr := parseRetryDelay(bodyBytes); parseErr == nil && retryAfter != nil { + sErr.retryAfter = retryAfter + } + } + err = sErr return nil, err } @@ -654,7 +684,13 @@ func (e *AntigravityExecutor) ExecuteStream(ctx context.Context, auth *cliproxya switch { case lastStatus != 0: - err = statusErr{code: lastStatus, msg: string(lastBody)} + sErr := statusErr{code: lastStatus, msg: string(lastBody)} + if lastStatus == http.StatusTooManyRequests { + if retryAfter, parseErr := parseRetryDelay(lastBody); parseErr == nil && retryAfter != nil { + sErr.retryAfter = retryAfter + } + } + err = sErr case lastErr != nil: err = lastErr default: @@ -795,12 +831,24 @@ func (e *AntigravityExecutor) CountTokens(ctx context.Context, auth *cliproxyaut log.Debugf("antigravity executor: rate limited on base url %s, retrying with fallback base url: %s", baseURL, baseURLs[idx+1]) continue } - return cliproxyexecutor.Response{}, statusErr{code: httpResp.StatusCode, msg: string(bodyBytes)} + sErr := statusErr{code: httpResp.StatusCode, msg: string(bodyBytes)} + if httpResp.StatusCode == http.StatusTooManyRequests { + if retryAfter, parseErr := parseRetryDelay(bodyBytes); parseErr == nil && retryAfter != nil { + sErr.retryAfter = retryAfter + } + } + return cliproxyexecutor.Response{}, sErr } switch { case lastStatus != 0: - return cliproxyexecutor.Response{}, statusErr{code: lastStatus, msg: string(lastBody)} + sErr := statusErr{code: lastStatus, msg: string(lastBody)} + if lastStatus == http.StatusTooManyRequests { + if retryAfter, parseErr := parseRetryDelay(lastBody); parseErr == nil && retryAfter != nil { + sErr.retryAfter = retryAfter + } + } + return cliproxyexecutor.Response{}, sErr case lastErr != nil: return cliproxyexecutor.Response{}, lastErr default: @@ -963,7 +1011,13 @@ func (e *AntigravityExecutor) refreshToken(ctx context.Context, auth *cliproxyau } if httpResp.StatusCode < http.StatusOK || httpResp.StatusCode >= http.StatusMultipleChoices { - return auth, statusErr{code: httpResp.StatusCode, msg: string(bodyBytes)} + sErr := statusErr{code: httpResp.StatusCode, msg: string(bodyBytes)} + if httpResp.StatusCode == http.StatusTooManyRequests { + if retryAfter, parseErr := parseRetryDelay(bodyBytes); parseErr == nil && retryAfter != nil { + sErr.retryAfter = retryAfter + } + } + return auth, sErr } var tokenResp struct {