From f5a3bb4419175452512fdd348a3cc745f8492422 Mon Sep 17 00:00:00 2001 From: Mish Ushakov <10400064+mishushakov@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:25:24 +0200 Subject: [PATCH] fix(python-sdk): stop leaking per-call proxy pools in volume content clients MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The volume content client factories passed both `proxy` and the shared cached `transport` to httpx. When a proxy was configured, httpx mounted a fresh, never-closed proxy transport per client — one per volume operation — bypassing the cached transport entirely. Drop the client-level `proxy`; it is already baked into the cached transport. Also add connect-level retries (E2B_CONNECTION_RETRIES, default 3) to the volume transports, matching the core API and envd transports. Co-Authored-By: Claude Fable 5 --- .changeset/python-volume-proxy-transport.md | 11 +++++++++++ .../python-sdk/e2b/volume/client_async/__init__.py | 6 ++++-- .../python-sdk/e2b/volume/client_sync/__init__.py | 6 ++++-- 3 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 .changeset/python-volume-proxy-transport.md diff --git a/.changeset/python-volume-proxy-transport.md b/.changeset/python-volume-proxy-transport.md new file mode 100644 index 0000000000..34a49d3fe7 --- /dev/null +++ b/.changeset/python-volume-proxy-transport.md @@ -0,0 +1,11 @@ +--- +"@e2b/python-sdk": patch +--- + +fix(python-sdk): stop leaking per-call proxy connection pools in volume content clients + +The volume content clients passed both `proxy` and the shared cached +`transport` to httpx, so with a proxy configured every volume operation mounted +a fresh, never-closed proxy transport. The proxy is already part of the cached +transport, so the client-level `proxy` argument is now dropped. Volume +transports also gained connect-level retries, matching the other transports. diff --git a/packages/python-sdk/e2b/volume/client_async/__init__.py b/packages/python-sdk/e2b/volume/client_async/__init__.py index 0df74903c0..db4fe8411e 100644 --- a/packages/python-sdk/e2b/volume/client_async/__init__.py +++ b/packages/python-sdk/e2b/volume/client_async/__init__.py @@ -7,7 +7,7 @@ from httpx import Limits from httpx._types import ProxyTypes -from e2b.api import make_async_logging_event_hooks +from e2b.api import connection_retries, make_async_logging_event_hooks from e2b.api.metadata import default_headers from e2b.exceptions import AuthenticationException from e2b.volume.client.client import AuthenticatedClient as AsyncVolumeApiClient @@ -47,7 +47,8 @@ def get_api_client(config: VolumeConnectionConfig, **kwargs) -> AsyncVolumeApiCl httpx.Timeout(request_timeout) if request_timeout is not None else None ), httpx_args={ - "proxy": config.proxy, + # The proxy lives in the cached transport; passing `proxy` here too + # would mount a fresh, never-closed proxy transport per client. "transport": get_transport(config), "event_hooks": make_async_logging_event_hooks(config.logger), }, @@ -82,6 +83,7 @@ def get_transport(config: VolumeConnectionConfig) -> AsyncTransportWithLogger: transport = AsyncTransportWithLogger( limits=limits, proxy=config.proxy, + retries=connection_retries, ) loop_instances[key] = transport diff --git a/packages/python-sdk/e2b/volume/client_sync/__init__.py b/packages/python-sdk/e2b/volume/client_sync/__init__.py index 4ceceda321..d5dff28391 100644 --- a/packages/python-sdk/e2b/volume/client_sync/__init__.py +++ b/packages/python-sdk/e2b/volume/client_sync/__init__.py @@ -6,7 +6,7 @@ from httpx import Limits from httpx._types import ProxyTypes -from e2b.api import make_logging_event_hooks +from e2b.api import connection_retries, make_logging_event_hooks from e2b.api.metadata import default_headers from e2b.exceptions import AuthenticationException from e2b.volume.client.client import AuthenticatedClient as VolumeApiClient @@ -46,7 +46,8 @@ def get_api_client(config: VolumeConnectionConfig, **kwargs) -> VolumeApiClient: httpx.Timeout(request_timeout) if request_timeout is not None else None ), httpx_args={ - "proxy": config.proxy, + # The proxy lives in the cached transport; passing `proxy` here too + # would mount a fresh, never-closed proxy transport per client. "transport": get_transport(config), "event_hooks": make_logging_event_hooks(config.logger), }, @@ -74,6 +75,7 @@ def get_transport(config: VolumeConnectionConfig) -> TransportWithLogger: transport = TransportWithLogger( limits=limits, proxy=config.proxy, + retries=connection_retries, ) instances[key] = transport TransportWithLogger._thread_local.instances = instances