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.APIErrorand*genai.APIErrorwithCode == 429as 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:andstatus: resource_exhausted. google.golang.org/genaimoves 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
go test ./pkg/providers/provider/ -run 'TestRateLimit|TestWrapper_Recognizes'go test ./pkg/providers/...,go vet,golangci-lint run ./pkg/providers/...- Live: point
GEMINI_SERVER_URLat a local proxy that returns Gemini's 429 body for the first 3generateContentcalls 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".
- Gemini 429 detected as a value, a pointer, wrapped with
- 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 fmtandgo 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.