Skip to content

fix(streaming): wrap mid-stream request errors - #3827

Open
heri-espino wants to merge 3 commits into
openai:mainfrom
heri-espino:fix/issue-3811-stream-transport-errors
Open

fix(streaming): wrap mid-stream request errors#3827
heri-espino wants to merge 3 commits into
openai:mainfrom
heri-espino:fix/issue-3811-stream-transport-errors

Conversation

@heri-espino

@heri-espino heri-espino commented Sep 9, 2026

Copy link
Copy Markdown
  • I understand that this repository is auto-generated and my pull request may not be merged

Changes being requested

Fixes #3811.

HTTPX request errors raised while consuming a streaming response currently escape as raw HTTPX/HTTPX2 exceptions instead of the SDK's APIError hierarchy.

This change makes streaming error handling consistent with the initial request path:

  • timeout request errors are wrapped as APITimeoutError
  • other HTTPX RequestErrors are wrapped as APIConnectionError
  • the original HTTPX exception is preserved as __cause__
  • sync and async streams behave consistently

The compatibility helper covers both httpx2 and a loaded legacy httpx module.

Using RequestError rather than only TransportError also covers decoding failures such as DecodingError, while avoiding a broad except Exception that would incorrectly convert unrelated parsing or application errors into APIConnectionError.

The Assistants streaming helpers have an existing compatibility contract where timeout callbacks receive and re-raise the underlying HTTPX exception. This patch preserves that behavior by unwrapping the request error from the SDK exception only in the Assistants event-handler layer.

This change intentionally does not retry partially consumed streams, since retry/resumption semantics after chunks have already been emitted are a separate concern.

Tests

Added regression coverage for sync and async streams for:

  • ReadTimeoutAPITimeoutError
  • RemoteProtocolErrorAPIConnectionError
  • DecodingErrorAPIConnectionError

Also verified the existing Assistants timeout callback behavior remains unchanged.

Validation completed locally:

  • targeted regression tests: 7 passed
  • ./scripts/lint: passed
  • full Pydantic v2 suite: 11,308 passed, 144 skipped
  • full Pydantic v1 suite: 11,294 passed, 158 skipped
  • git diff --check: passed

Additional context & links

This overlaps with #3813, #3814, and #3818.

This version incorporates issues identified in the existing review discussions:

@heri-espino
heri-espino requested a review from a team as a code owner September 9, 2026 13:01

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f62ba8948f

ℹ️ 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".

Comment thread src/openai/lib/streaming/_assistants.py Outdated
Comment on lines +422 to +425
except APITimeoutError as exc:
error = _request_error_from_api_error(exc)
self.on_timeout()
self.on_exception(error or exc)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Limit unwrapping to stream-iterator failures

When a user hook called by _emit_sse_event makes another SDK request that fails with APITimeoutError or APIConnectionError, this handler also catches that exception; because its cause is an HTTPX RequestError, _request_error_from_api_error mistakes it for a failure from for event in stream, passes the raw HTTPX exception to on_exception, and subsequently re-raises that raw exception (while the timeout case also invokes on_timeout). This unintentionally changes the callback's exception contract rather than only preserving compatibility for transport failures from the streamed response; iterator advancement and hook execution need separate exception handling, including in the mirrored async branch.

AGENTS.md reference: AGENTS.md:L5-L8

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 80686f3624

ℹ️ 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".

Comment thread src/openai/_streaming.py Outdated
Comment on lines +110 to +113
except timeout_exceptions() as err:
raise APITimeoutError(request=response.request) from err
except request_exceptions() as err:
raise APIConnectionError(request=response.request) from err

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Catch request errors only while advancing the SSE iterator

Although the Assistants hook handling is now separated, the fresh core loop still places process_data(...) inside the new catch scope. When a custom ModelBuilderProtocol.build implementation or Pydantic validator makes an SDK call that raises APITimeoutError/APIConnectionError with an HTTPX RequestError cause, these handlers catch and replace it with a new error associated with the streaming request; this changes the application callback's exception identity and request context in both sync and async streams. Restrict these catches to iterator advancement, as was done for the Assistants layer.

AGENTS.md reference: AGENTS.md:L5-L8

Useful? React with 👍 / 👎.

@heri-espino

Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 1be10fc663

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 1be10fc663

ℹ️ 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".

@heri-espino

Copy link
Copy Markdown
Author

Looks ready now :) Codex re-re-reviewed the latest commit and the security review is clean. Thanks for taking a look!

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.

Transport errors while consuming a stream escape as raw httpx exceptions instead of APITimeoutError / APIConnectionError

1 participant