Skip to content

Unable to completely disable API retries for interactions API #3260

Description

@FlippFuzz

Environment details

  • Programming language: Python
  • OS: Ubuntu 26.04
  • Language runtime version: Python 3.14.4
  • Package version: google-genai-2.34.0

Current behavior:

  • I set HttpRetryOptions(attempts=0) or HttpRetryOptions(attempts=1)
  • API always retries ONCE, sleeping for the retry-after HTTP header between the retry attempts.

Why is this an issue:

When you run out of free tier API rate-limits, you get an extremely long retry-after such as 8h.

{'http.response.body.text': {'error': {'message': 'Rate limit exceeded for model gemini-3.8-flash (limit: 20 requests per day on Free Tier). Please retry in 7h40m27s or upgrade your tier at https://ai.dev/rate-limit.', 'code': 'too_many_requests'}}}

It is not ideal for the interactions API to block for 8h.
I would like the API to return immediately (zero retries).


Steps to reproduce

  • We point interactions.create to a mock HTTP server.
  • The mock HTTP server prints that it received a "POST" from the interactions API, and then returns 429 with a 60s retry-after header.
import asyncio
from http.server import BaseHTTPRequestHandler, HTTPServer
import threading
import time

from google import genai
from google.genai import types


RETRY_AFTER=60

# 1. Spin up a minimal local server that returns HTTP 429 with Retry-After
class Mock429Handler(BaseHTTPRequestHandler):

    def do_POST(self):
        print(f"[{time.strftime('%X')}] Mock server received POST, returning 429 with Retry-After: {RETRY_AFTER}s...")
        self.send_response(429)
        self.send_header("Content-Type", "application/json")
        self.send_header("Retry-After", RETRY_AFTER)
        self.end_headers()
        self.wfile.write(b'{"error": {"code": "too_many_requests", "message": "Daily quota exceeded"}}')

    def log_message(self, format, *args):
        pass  # Suppress default server logs


server = HTTPServer(("127.0.0.1", 8999), Mock429Handler)
threading.Thread(target=server.serve_forever, daemon=True).start()


# 2. Reproduce the bug in google-genai
async def main():
    print(f"[{time.strftime('%X')}] Initializing genai.Client with attempts=0...")

    client = genai.Client(
        api_key="fake-key",
        http_options=types.HttpOptions(
            base_url="http://127.0.0.1:8999",
            retry_options=types.HttpRetryOptions(attempts=0),  # Attempting to disable retries. Both 0 and 1 yield the same result.
        ),
    )

    print(f"[{time.strftime('%X')}] Calling client.aio.interactions.create()...")
    print(
        "Expected: Fail immediately.\n"
        "Actual:   API waits for retry-after and retries once despite me disabling retries."
    )

    await client.aio.interactions.create(
        model="gemini-3.8-flash",
        input="Hello",
    )
    print("End of interactions.create")


asyncio.run(main())
python standalone.py
[01:07:32] Initializing genai.Client with attempts=0...
[01:07:32] Calling client.aio.interactions.create()...
Expected: Fail immediately.
Actual:   API waits for retry-after and retries once despite me disabling retries.
[01:07:33] Mock server received POST, returning 429 with Retry-After: 60s...
[01:08:33] Mock server received POST, returning 429 with Retry-After: 60s...
Traceback (most recent call last):
  File "/home/ubuntu/test/venv/lib/python3.14/site-packages/google/genai/_gaos/interactions.py", line 1923, in create
    return await _speakeasy_parse_response(http_res)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/test/venv/lib/python3.14/site-packages/google/genai/_gaos/interactions.py", line 1837, in _speakeasy_parse_response
    raise errors.CreateInteractionClientError(
        response_data, http_res, http_res_text
    )
google.genai._gaos.errors.createinteraction.CreateInteractionClientError: Daily quota exceeded

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/ubuntu/test/standalone.py", line 56, in <module>
    asyncio.run(main())
    ~~~~~~~~~~~^^^^^^^^
  File "/usr/lib/python3.14/asyncio/runners.py", line 204, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "/usr/lib/python3.14/asyncio/runners.py", line 127, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/usr/lib/python3.14/asyncio/base_events.py", line 719, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "/home/ubuntu/test/standalone.py", line 49, in main
    await client.aio.interactions.create(
    ...<2 lines>...
    )
  File "/home/ubuntu/test/venv/lib/python3.14/site-packages/google/genai/_gaos/google_genai.py", line 655, in create
    response = await send_once()
               ^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/test/venv/lib/python3.14/site-packages/google/genai/_gaos/google_genai.py", line 710, in _send_create_once
    return await async_wrap_sdk_call(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<3 lines>...
    )
    ^
  File "/home/ubuntu/test/venv/lib/python3.14/site-packages/google/genai/_gaos/lib/compat_errors.py", line 379, in async_wrap_sdk_call
    return await fn(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/test/venv/lib/python3.14/site-packages/google/genai/_gaos/interactions.py", line 1925, in create
    await response_helpers.raise_parse_error_async(
    ...<5 lines>...
    )
  File "/home/ubuntu/test/venv/lib/python3.14/site-packages/google/genai/_gaos/utils/response_helpers.py", line 112, in raise_parse_error_async
    raise parse_error from exc
google.genai._gaos.lib.compat_errors.RateLimitError: Error code: 429 - {'error': {'code': 'too_many_requests', 'message': 'Daily quota exceeded'}}

Notice that the mock server received two HTTP POSTs from the interactions API.

[01:07:33] Mock server received POST, returning 429 with Retry-After: 60s...
[01:08:33] Mock server received POST, returning 429 with Retry-After: 60s...

Expectation: Only 1 HTTP POST because I set HttpRetryOptions(attempts=0).

Activity

  1. added
    type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.
    priority: p2Moderately-important priority. Fix may not be included in next release.
    on Nov 4, 2026
  2. added a commit that references this issue on Nov 4, 2026
    1671a3f
  3. added a commit that references this issue on Nov 4, 2026
    10fdcce
  4. Venkaiahbabuneelam commented on Nov 4, 2026

    @Venkaiahbabuneelam

    Hi @FlippFuzz, thanks for the reporting this issue, we have reproduced this bug and confirmed that interactions mistakenly treats attempts=1 (and attempts=0, which gets normalised to 1) as 1 extra retry instead of 1 total request, and we have filed an internal bug.

    Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

priority: p2Moderately-important priority. Fix may not be included in next release.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions