Skip to content

fix: retry connection errors in the interactions client - #3205

Open
fcakyon wants to merge 5 commits into
googleapis:mainfrom
fcakyon:fix-interactions-transport-retries
Open

fcakyon wants to merge 5 commits into
googleapis:mainfrom
fcakyon:fix-interactions-transport-retries

Conversation

@fcakyon

@fcakyon fcakyon commented Oct 23, 2026

Copy link
Copy Markdown

Fixes #3204.

client.interactions never retries httpx.ConnectError or other transport failures, even though _translate_retry_config sets retry_connection_errors=True. CompatErrorHook.after_error runs inside the per-attempt do() in BaseSDK.do_request and turns the httpx error into APIConnectionError. _is_transport_error in the generated retries.py does not recognize that type, so the retry loop raises it as a PermanentError on the first attempt.

  • after_error now returns errors unchanged. Translation happens once, at the public boundary, in wrap_sdk_call / async_wrap_sdk_call, which the bridge methods already call (raw and streaming responses go through _RawResponseAccessorProxy). Callers still get APIConnectionError / APITimeoutError with the httpx error as __cause__.
  • environments.files.upload was the one bridge path that called generated code (self._internal.start_upload, sync and async) without wrap_sdk_call. It is wrapped now, so it keeps raising APIConnectionError.
  • Both changes are in hand-maintained files, since basesdk.py and retries.py are Speakeasy output.
  • With HttpRetryOptions(attempts=3) and a transport that always raises ConnectError: 1 attempt before, 4 after, the same as a 503. Sync, async, and the httpx2 client all behave this way.
  • Behavior change: _translate_retry_config defaults to 3 retries when retry_options is unset, so default clients now retry connection errors and timeouts (httpx.TimeoutException) too. The models path already retries these when HttpRetryOptions is set (fix(retry): retry on httpx.TimeoutException with HttpRetryOptions #2345, fix: Honor HttpRetryOptions for aiohttp connection errors in async requests #3186).
  • New tests in test_httpx_compat.py: the sync and async interactions tests fail on main (assert 1 > 1), and the upload test fails without the new wrap_sdk_call (raw httpx.ConnectError).
-        if error is None:
-            return response, error
-        return response, wrap_sdk_error(error)  # type: ignore[return-value]
+        # Runs once per attempt inside the retry loop, which only retries raw
+        # httpx errors, so `wrap_sdk_call` translates after the last attempt.
+        return response, error

CompatErrorHook wrapped httpx errors before the retry loop checked them.
@google-cla

google-cla Bot commented Oct 23, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@fcakyon fcakyon changed the title Retry connection errors in the interactions client fix: retry connection errors in the interactions client Oct 23, 2026
@Venkaiahbabuneelam Venkaiahbabuneelam self-assigned this Oct 26, 2026
@Venkaiahbabuneelam Venkaiahbabuneelam added the size:L Code changes between 40-100 lines label Oct 26, 2026

@chrikrah chrikrah left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fcakyon from my perspective this can be merged. Each new test fails when I put back its half of the change.

# git archive 8fc7787 (base 92f72b3), google.genai.__file__ = <tree>/google/genai/__init__.py, Python 3.12
$ python -m pytest google/genai/tests/interactions/test_httpx_compat.py -q -p no:cacheprovider
8 passed, 14 skipped                         # head 8fc7787
2 failed, 6 passed, 14 skipped               # compat_errors.py from 92f72b3
FAILED ...::test_interactions_retries_connection_errors
FAILED ...::test_async_interactions_retries_connection_errors
1 failed, 7 passed, 14 skipped               # google_genai.py from 92f72b3
FAILED ...::test_environment_file_upload_wraps_connection_errors

# default Client (no retry_options), MockTransport raising httpx.ReadTimeout on interactions.create
head 8fc7787: 4 x POST, APITimeoutError
base 92f72b3: 1 x POST, APITimeoutError
# not run: the 14 httpx2 cases, a live endpoint

non-blocking: base already sets retry_connection_errors=True and retries 408 and 5XX on create, so this makes the timeout half of that config take effect, and a timed-out create now goes out four times. Whether a default client retries a non-idempotent POST is for the API owners, not this PR. Would you add the ReadTimeout probe as a test, so that call has the count in front of it?

@fcakyon

fcakyon commented Oct 28, 2026 •

Copy link
Copy Markdown
Author

thanks @chrikrah, added the default ReadTimeout case asserting four POST attempts and APITimeoutError

This branch has not been deployed

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

Labels

size:L Code changes between 40-100 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Interactions API does not retry connection errors despite HttpRetryOptions

3 participants