Skip to content

Ensure mounted transports are closed even if main transport raises - #3769

Open
bysiber wants to merge 1 commit into
encode:masterfrom
bysiber:fix/ensure-transport-cleanup-on-close
Open

Ensure mounted transports are closed even if main transport raises#3769
bysiber wants to merge 1 commit into
encode:masterfrom
bysiber:fix/ensure-transport-cleanup-on-close

Conversation

@bysiber

@bysiber bysiber commented Feb 20, 2026

Copy link
Copy Markdown

When closing a client with proxy mounts, if the main transport's close()/__exit__() raises an exception, the mounted proxy transports would never be cleaned up. This wraps transport cleanup in try/finally to ensure all transports are properly closed.

Affects Client.close(), Client.__exit__(), AsyncClient.aclose(), and AsyncClient.__aexit__().

When closing a client with proxy mounts, if the main transport's
close/exit raises an exception, the mounted transports would never
be cleaned up. This could lead to leaked connections.

Wrap the main transport cleanup in try/finally to ensure mounted
transports are always properly closed.
Comment thread httpx/_client.py
finally:
for transport in self._mounts.values():
if transport is not None:
transport.close()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cleanup still stops at the first mounted transport that raises, so later mounts remain open. On ff7190e, I used the public Client(transport=..., mounts=...) API with a main transport whose close() raises, a first mounted transport whose close() also raises, and a second mounted transport that records cleanup. client.close() produced calls=['main', 'first']; the second mount was never closed, and the first mount's exception replaced the original main-transport exception. The async loops have the same short-circuit behavior.

To meet this PR's all-transports cleanup goal, each mounted cleanup needs to be attempted independently while preserving or aggregating the raised exceptions.

Disclosure: I ran the changed client module through Bandit → SARIF → Lumi Trace at NOQT (which ranked _client.py first), then verified this with the focused runtime repro above.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants