You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: retry on all TransportError subclasses (ReadError, WriteError, etc.) (#334)
## Summary
- Replaces individual `except` blocks for `ConnectError`,
`RemoteProtocolError`, and `TimeoutException` with a single catch for
their parent class `httpx.TransportError`
- This covers `ReadError` (TCP connection reset mid-response with empty
message), `WriteError`, and all other transport-level failures
- Previously, `ReadError` fell through to the catch-all `Exception`
handler and was wrapped as `PermanentError`, failing immediately without
retry
## Context
Follow-up to #332. After deploying the `RemoteProtocolError` fix, we
observed `httpx.ReadError` (empty message) failures when api pods
crashed mid-response. The TCP connection was reset during the response
read phase, which httpx classifies as `ReadError` rather than
`RemoteProtocolError`.
The httpx exception hierarchy:
```
TransportError
├── ConnectError (was retried)
├── RemoteProtocolError (was retried since #332)
├── ReadError (was NOT retried — now fixed)
├── WriteError (was NOT retried — now fixed)
├── PoolTimeout (was NOT retried — now fixed)
└── ...
TimeoutException (was retried, subclass of TransportError)
├── ConnectTimeout
├── ReadTimeout
├── WriteTimeout
└── PoolTimeout
```
Catching `TransportError` is the correct level — all transport errors
are transient and should be retried when `retry_connection_errors=True`.
## Test plan
- [x] Parametrized tests for all TransportError subclasses (sync +
async)
- [ ] Each subclass retried when `retry_connection_errors=True`
- [ ] Each subclass raises immediately when
`retry_connection_errors=False`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Expands which network failures are treated as retryable, which can
change error/latency behavior for callers and potentially mask
persistent transport issues until backoff is exhausted.
>
> **Overview**
> **Broadened retry handling for transport failures.** The retry wrapper
now catches `httpx.TransportError` in both sync and async paths, so
additional transport-level errors (e.g. `ReadError`, `WriteError`, and
timeout subclasses) are retried when `retry_connection_errors=True`
instead of being treated as permanent.
>
> Tests were updated to parameterize across multiple `TransportError`
subclasses for both sync and async retry behavior, and the package
version/release notes were bumped to `0.42.12`.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
bdd403c. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+9Lines changed: 9 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,3 +1,12 @@
1
+
## 0.42.12
2
+
3
+
### Enhancements
4
+
5
+
### Features
6
+
7
+
### Fixes
8
+
* Retry on all `httpx.TransportError` subclasses (including `ReadError`, `WriteError`, `ConnectError`, `RemoteProtocolError`, and all timeout types) when `retry_connection_errors=True`. Previously only `ConnectError`, `RemoteProtocolError`, and `TimeoutException` were retried — `ReadError` (TCP connection reset mid-response) was treated as permanent.
0 commit comments