Confirm this is an issue with the Python library and not an underlying OpenAI API
Describe the bug
_base_client wraps the initial send: httpx.TimeoutException becomes APITimeoutError, other transport errors become APIConnectionError, and both go through the retry loop. Once the response is streaming, _streaming.py iterates the response with no handling at all, so a read timeout or a dropped connection mid-stream surfaces as the raw httpx.ReadTimeout / httpx.RemoteProtocolError (httpx2.* on 3.x). That is not an APIError, and max_retries is not consulted.
So except openai.APIError around a streaming call misses the most common streaming failure, and a client with max_retries=2 makes exactly one request.
Expected: the same wrapping as the initial send, APITimeoutError / APIConnectionError, so the exception hierarchy holds for the whole call. Whether a partially consumed stream can be retried is a separate question; wrapping alone fixes the catch side. anthropic-sdk-python has the same gap, same generated base: anthropics/anthropic-sdk-python#1919
To Reproduce
- Run the script below. No network, the mock transport yields one chunk and then raises
ReadTimeout.
- The raw
httpx.ReadTimeout escapes, isinstance(e, openai.APIError) is False, one request was made.
Code snippets
import platform
import sys
import openai
try:
import httpx2 as httpx
except ImportError:
import httpx
FIRST = b'data: {"id":"c1","object":"chat.completion.chunk","created":0,"model":"gpt-5.2","choices":[{"index":0,"delta":{"role":"assistant","content":"hi"},"finish_reason":null}]}\n\n'
requests_seen = 0
class DiesMidStream(httpx.SyncByteStream):
def __iter__(self):
yield FIRST
raise httpx.ReadTimeout("timed out while reading the stream")
def handler(request):
global requests_seen
requests_seen += 1
return httpx.Response(200, headers={"content-type": "text/event-stream"}, stream=DiesMidStream())
client = openai.OpenAI(api_key="x", http_client=httpx.Client(transport=httpx.MockTransport(handler)), max_retries=2)
print(f"python {sys.version.split()[0]} {platform.system()}, openai {openai.__version__}, httpx {httpx.__version__}")
try:
for _ in client.chat.completions.create(model="gpt-5.2", messages=[{"role": "user", "content": "hi"}], stream=True):
pass
except Exception as e: # noqa: BLE001
print(f"escaped: {type(e).__module__}.{type(e).__name__}: {e}")
print(f"isinstance(e, openai.APIError) = {isinstance(e, openai.APIError)}")
print(f"requests made with max_retries=2: {requests_seen}")
Output (python 3.12.13, macOS):
openai 2.26.0, httpx 0.28.1
escaped: httpx.ReadTimeout: timed out while reading the stream
isinstance(e, openai.APIError) = False
requests made with max_retries=2: 1
openai 3.8.0, httpx 2.12.0
escaped: httpx2.ReadTimeout: timed out while reading the stream
isinstance(e, openai.APIError) = False
requests made with max_retries=2: 1
OS
macOS
Python version
Python v3.12.13
Library version
openai v2.26.0 and v3.8.0
Confirm this is an issue with the Python library and not an underlying OpenAI API
Describe the bug
_base_clientwraps the initial send:httpx.TimeoutExceptionbecomesAPITimeoutError, other transport errors becomeAPIConnectionError, and both go through the retry loop. Once the response is streaming,_streaming.pyiterates the response with no handling at all, so a read timeout or a dropped connection mid-stream surfaces as the rawhttpx.ReadTimeout/httpx.RemoteProtocolError(httpx2.*on 3.x). That is not anAPIError, andmax_retriesis not consulted.So
except openai.APIErroraround a streaming call misses the most common streaming failure, and a client withmax_retries=2makes exactly one request.Expected: the same wrapping as the initial send,
APITimeoutError/APIConnectionError, so the exception hierarchy holds for the whole call. Whether a partially consumed stream can be retried is a separate question; wrapping alone fixes the catch side. anthropic-sdk-python has the same gap, same generated base: anthropics/anthropic-sdk-python#1919To Reproduce
ReadTimeout.httpx.ReadTimeoutescapes,isinstance(e, openai.APIError)is False, one request was made.Code snippets
Output (python 3.12.13, macOS):
OS
macOS
Python version
Python v3.12.13
Library version
openai v2.26.0 and v3.8.0