fix: raise APIError for top-level Responses API error events during streaming - #3587
fix: raise APIError for top-level Responses API error events during streaming#3587mayuriphad wants to merge 1 commit into
Conversation
…treaming Stream.__stream__ / AsyncStream.__stream__ only raised an APIError when the SSE event body had a nested "error" key. Per the Responses API streaming spec, the "error" event carries its fields (type, code, message, param, sequence_number) directly on the event body with no nested "error" key, so these events were silently yielded as regular data instead of raising, leading to confusing downstream validation errors instead of a clear APIError with the server's message. Fixes openai#2487
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 698bbc5c9b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| else: | ||
| data = sse.json() | ||
| if is_mapping(data) and data.get("error"): | ||
| if is_mapping(data) and (data.get("error") or sse.event == "error"): |
There was a problem hiding this comment.
Preserve Assistant error events
When this shared stream path is used by the Assistants endpoints, synthesize_event_and_data=True causes a raw SSE event: error body to be wrapped into the documented AssistantStreamEvent shape (event == "error", data: ErrorObject). This new condition raises before that wrapping, so an Assistants stream error that consumers previously could handle via iteration or on_event now aborts as APIError; please scope the new top-level handling to Responses streams or skip it when synthesizing event/data. This is an unrelated exported-stream behavior change from a handwritten core helper.
Useful? React with 👍 / 👎.
Summary
Fixes #2487.
Stream.__stream__/AsyncStream.__stream__insrc/openai/_streaming.pyonly raise anAPIErrorwhen the SSE event body has a nested"error"key:But per the Responses API streaming error spec, the
errorevent carries its fields (type,code,message,param,sequence_number) directly on the event body -- there is no nested"error"key:{"type": "error", "code": "...", "message": "Something went wrong", "param": null, "sequence_number": 1}Because
data.get("error")isNonefor these events, the condition never matches, so the event is silently passed toprocess_data(...)as if it were ordinary stream data instead of raising a clearAPIError. Depending on the expected type being cast to, this can surface as a confusing Pydantic validation error deep in the stack instead of the server's actual error message.Fix
In both the sync
StreamandAsyncStream__stream__methods, also treatsse.event == "error"as an error condition, falling back to the top-level event body for the message/body when there's no nested"error"key (kept for backwards compatibility with any caller still relying on that shape):This is a minimal, targeted change -- no unrelated refactors.
Tests
Added regression tests in
tests/test_streaming.py(for bothStreamandAsyncStream, via the existingsyncparametrization):test_response_error_event_raises_api_error-- a top-levelresponse.error-shaped SSEerrorevent raisesAPIErrorwith the server'smessage.test_response_error_event_without_message_uses_default-- the same event without amessagefield raisesAPIErrorwith the existing default message.Both tests fail on
main(DID NOT RAISE APIError) and pass with this fix.ruff checkandruff format --checkpass on both changed files.