Skip to content

fix: retry Gemini 429 rate-limit errors in provider wrapper - #453

Open
lalith0407 wants to merge 1 commit into
vxcontrol:mainfrom
lalith0407:fix/gemini-429-retry
Open

lalith0407 wants to merge 1 commit into
vxcontrol:mainfrom
lalith0407:fix/gemini-429-retry

Conversation

@lalith0407

Copy link
Copy Markdown

Description of the Change

Problem

isTooManyRequestsError doesn't recognize Gemini's 429. The genai client returns genai.APIError by value (Error 429, Message: ..., Status: RESOURCE_EXHAUSTED). So the wrapper's 429 retry never runs for Gemini, and the first rate limit fails the call. Related to #446 / #447, where that unretried 429 hit the task title.

Solution

  • In the existing unwrap loop, treat genai.APIError and *genai.APIError with Code == 429 as a rate limit, the same way the AWS types are checked.
  • Text fallback for errors that were flattened to strings: also match error 429, message: and status: resource_exhausted.
  • google.golang.org/genai moves from an indirect to a direct dependency (go mod tidy, same version, go.sum unchanged).

Closes #452

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)

Areas Affected

  • External Integrations (LLM/Search APIs)

Testing and Verification

Test Configuration

PentAGI Version: 2.2.0-ce (843dfeb)
Docker Version: 29.9.1
Host OS: Linux
LLM Provider: Gemini
Enabled Features: none

Test Steps

  1. go test ./pkg/providers/provider/ -run 'TestRateLimit|TestWrapper_Recognizes'
  2. go test ./pkg/providers/..., go vet, golangci-lint run ./pkg/providers/...
  3. Live: point GEMINI_SERVER_URL at a local proxy that returns Gemini's 429 body for the first 3 generateContent calls and forwards the rest, then start a Gemini flow.

Test Results

  • Unit tests:
    • Gemini 429 detected as a value, a pointer, wrapped with %w, as text, and with only the code set.
    • OpenAI and Anthropic 429 text: still detected.
    • Not treated as a rate limit: Gemini 400/500/503, OpenAI 500, context.Canceled, context.DeadlineExceeded, nil, and text with "4290"/"call_429".
  • Live, unpatched: 3 requests 5s apart, then the flow failed after 10s.
  • Live, patched: 429s at +0s, +5s, +11s, the 4th call succeeded, and the flow finished on Gemini.

Security Considerations

None.

Performance Impact

A rate-limited Gemini call now waits through the wrapper backoff (up to 10 tries, 5s plus 1s more each try) instead of failing at once, the same as the other providers.

Documentation Updates

None.

Deployment Notes

None.

Checklist

  • I have added tests to cover my changes
  • All new and existing tests pass
  • I have run go fmt and go vet (for Go code)
  • Changes are backward compatible
  • Dependencies are properly updated

Additional Notes

Gemini includes a RetryInfo retryDelay (for example 11s) in APIError.Details. The wrapper still uses its fixed backoff, and this PR doesn't change that.

HUMAN:

After my task-title PR (#447) I kept digging into why my Gemini free-tier flows died on the very first 429. It turns out the wrapper already retries rate limits, but its check only knows OpenAI-style and AWS errors, so Gemini's 429 never matches. I added a check for Gemini's error type, with a text fallback, and kept everything else the same. Happy to also skip retries on daily-quota 429s or honor Gemini's retry delay if you'd like.

isTooManyRequestsError only matched the AWS error types and the
"status code: 429" / "rate limit exceeded" text other clients produce.
The genai client behind the Gemini provider returns genai.APIError
("Error 429, Message: ..., Status: RESOURCE_EXHAUSTED"), so a Gemini rate
limit was never retried and failed the call on the first 429. Detect the
typed error by its status code, and its text form, so Gemini gets the same
backoff as the other providers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gemini 429 rate limits are not retried

1 participant