Skip to content

fix: wrap mid-stream httpx.TransportError as APIConnectionError#1552

Open
blut-agent wants to merge 2 commits into
anthropics:mainfrom
blut-agent:fix/stream-transport-error
Open

fix: wrap mid-stream httpx.TransportError as APIConnectionError#1552
blut-agent wants to merge 2 commits into
anthropics:mainfrom
blut-agent:fix/stream-transport-error

Conversation

@blut-agent
Copy link
Copy Markdown

What

Wrap mid-stream httpx.TransportError (during SSE body iteration) as anthropic.APIConnectionError, in both Stream._iter_events and AsyncStream._iter_events.

Why

Mid-stream transport drops (RemoteProtocolError, ReadError, ConnectError) currently leak through as bare httpx exceptions because the SDK's wrapping in _base_client._request only covers the pre-body request. Once the SSE 200 is sent and body iteration starts, there's no try/except in _iter_events.

This means customers' standard retry ladders:

except anthropic.APIConnectionError:
    retry()

…miss mid-stream drops. They have to know to also catch httpx.TransportError, which nobody discovers without debugging.

Changes

  • Stream._iter_events / AsyncStream._iter_events: wrap httpx.TransportError as APIConnectionError (same pattern as _base_client.py:1104); let TimeoutException pass through unchanged so it doesn't get double-wrapped (APITimeoutError is already an APIConnectionError subclass).
  • 2 tests: mid-stream RemoteProtocolError is wrapped (with __cause__ preserved); mid-stream ReadTimeout passes through.

Not in this PR

The follow-up — auto-retry the full request inside MessageStream.get_final_message() on mid-stream APIConnectionError — is a larger behavior change. This PR just makes the exception catchable with the right type.

File upload helpers were sharing mutable containers nested inside file
tuples, such as per-file headers dictionaries. This could cause bugs
when uploading multiple files or when the caller reuses file input
dictionaries.

- Add `_transform_file_tuple` helper that copies dict entries (headers)
  while preserving immutable file content references.
- Use the helper in both `_transform_file` and `_async_transform_file`.
- Add regression test for file tuple with mutable headers.

Fixes anthropics#1548
Wrap mid-stream httpx.TransportError (during SSE body iteration) as
anthropic.APIConnectionError, in both Stream._iter_events and
AsyncStream._iter_events.

Mid-stream transport drops (RemoteProtocolError, ReadError, ConnectError)
currently leak through as bare httpx exceptions because the SDK's wrapping
in _base_client._request only covers the pre-body request. Once the SSE 200
is sent and body iteration starts, there's no try/except in _iter_events.

This means customers' standard retry ladders:
  except anthropic.APIConnectionError:
      retry()
…miss mid-stream drops. They have to know to also catch httpx.TransportError,
which nobody discovers without debugging.

Changes:
- Stream._iter_events / AsyncStream._iter_events: wrap httpx.TransportError
  as APIConnectionError (same pattern as _base_client.py:1104); let
  TimeoutException pass through unchanged so it doesn't get double-wrapped
  (APITimeoutError is already an APIConnectionError subclass).
- 2 tests: mid-stream RemoteProtocolError is wrapped (with __cause__ preserved);
  mid-stream ReadTimeout passes through.
@blut-agent blut-agent requested a review from a team as a code owner May 16, 2026 07:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant