From f0ea16be90d60ecca6eb9a6ff4ba56ffe1fb45e4 Mon Sep 17 00:00:00 2001 From: yurekami Date: Mon, 29 Dec 2025 05:32:23 +0900 Subject: [PATCH] fix(streaming): move error event check before thread.* handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `sse.event == "error"` check was unreachable inside the `sse.event.startswith("thread.")` block since a string cannot simultaneously equal "error" and start with "thread.". This moves error event handling to a separate check before the thread.* event handling, ensuring error events are properly caught and raise APIError. Fixes #2796 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/openai/_streaming.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/openai/_streaming.py b/src/openai/_streaming.py index 61a742668a..d2bdcda6bc 100644 --- a/src/openai/_streaming.py +++ b/src/openai/_streaming.py @@ -60,11 +60,10 @@ def __stream__(self) -> Iterator[_T]: if sse.data.startswith("[DONE]"): break - # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data - if sse.event and sse.event.startswith("thread."): + # Check for error events first + if sse.event == "error": data = sse.json() - - if sse.event == "error" and is_mapping(data) and data.get("error"): + if is_mapping(data) and data.get("error"): message = None error = data.get("error") if is_mapping(error): @@ -78,6 +77,9 @@ def __stream__(self) -> Iterator[_T]: body=data["error"], ) + # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data + elif sse.event and sse.event.startswith("thread."): + data = sse.json() yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) else: data = sse.json() @@ -163,11 +165,10 @@ async def __stream__(self) -> AsyncIterator[_T]: if sse.data.startswith("[DONE]"): break - # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data - if sse.event and sse.event.startswith("thread."): + # Check for error events first + if sse.event == "error": data = sse.json() - - if sse.event == "error" and is_mapping(data) and data.get("error"): + if is_mapping(data) and data.get("error"): message = None error = data.get("error") if is_mapping(error): @@ -181,6 +182,9 @@ async def __stream__(self) -> AsyncIterator[_T]: body=data["error"], ) + # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data + elif sse.event and sse.event.startswith("thread."): + data = sse.json() yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) else: data = sse.json()