Skip to content

feat(streaming): normalize errors raised while reading streams - #3827

Merged
marcuswood-oai merged 4 commits into
openai:mainfrom
heri-espino:fix/issue-3811-stream-transport-errors
Sep 10, 2026
Merged

feat(streaming): normalize errors raised while reading streams#3827
marcuswood-oai merged 4 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
Contributor

Summary

A timeout or broken connection after streaming starts currently escapes as a raw HTTPX exception, bypassing except openai.APIError. Wrap request failures at the event-read boundary, preserving the original exception as __cause__ and closing the response.

Keep parsing and callback errors unchanged. Preserve existing Assistants event-handler and raw byte-stream behavior. Partially consumed streams are not retried.

Fixes #3811. Supersedes the overlapping fixes in #3813, #3814, and #3818.

Release note

Release as a minor version. Stream and AsyncStream now raise openai.APITimeoutError for read timeouts and openai.APIConnectionError for other HTTPX request failures, including decoding errors. Applications catching raw httpx or httpx2 exceptions during event streaming should catch these SDK exceptions instead. The underlying exception remains available through __cause__.

Validation

  • Merged current main and resolved the test-import conflict.
  • Six after-first-chunk regression cases fail on main and pass with this change.
  • 77 focused tests pass on each of Pydantic v1 and v2, including sync/async HTTPX2 and legacy HTTPX, response cleanup, no retries, and callback compatibility.
  • Broad offline suite: 3,658 passed, 132 skipped; large-payload contract: 1 passed, run sequentially.
  • Ruff, Mypy, and focused Pyright pass.
  • Custom-code budget passes: 6,827 / 10,000 lines.
  • Full diff security review found no unrelated changes or dependency, credential, or workflow modifications.

API/mock-server and network-dependent suites were excluded from the broad local run. The separate optional legacy aiohttp adapter test could not run because httpx_aiohttp is not installed.

@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
Contributor 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
Contributor Author

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

@marcuswood-oai marcuswood-oai changed the title fix(streaming): wrap mid-stream request errors feat(streaming): normalize errors raised while reading streams Sep 10, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-10T22:33:22.275054Z 6982b4b New commits
🔒 Security Review Completed 2026-09-10T22:33:33.063590Z 6982b4b New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Castiron custom code

✅ No new custom-code files detected.

43 mixed files remain; 0 existing customizations changed.

Compared ae41bc46cdd56982b4bb1fc0. Generated baselines verified.

43 existing customizations unchanged
  • api.md
  • scripts/castiron/README.md
  • scripts/castiron/custom_code_report.py
  • scripts/castiron/test_custom_code_report.py
  • src/openai/init.py
  • src/openai/_client.py
  • src/openai/resources/audio/transcriptions.py
  • src/openai/resources/audio/translations.py
  • src/openai/resources/beta/agents/sessions/sessions.py
  • src/openai/resources/beta/beta.py
  • src/openai/resources/beta/responses/responses.py
  • src/openai/resources/beta/threads/runs/runs.py
  • src/openai/resources/beta/threads/threads.py
  • src/openai/resources/chat/completions/completions.py
  • src/openai/resources/embeddings.py
  • src/openai/resources/files.py
  • src/openai/resources/live/forks.py
  • src/openai/resources/live/live.py
  • src/openai/resources/live/sideband.py
  • src/openai/resources/realtime/realtime.py
  • src/openai/resources/responses/responses.py
  • src/openai/resources/uploads/uploads.py
  • src/openai/resources/vector_stores/file_batches.py
  • src/openai/resources/vector_stores/files.py
  • src/openai/resources/videos.py
  • src/openai/resources/webhooks/init.py
  • src/openai/resources/webhooks/webhooks.py
  • src/openai/types/beta/agent_session_message.py
  • src/openai/types/chat/init.py
  • src/openai/types/chat/chat_completion_message_tool_call.py
  • src/openai/types/fine_tuning/fine_tuning_job_integration.py
  • src/openai/types/responses/init.py
  • src/openai/types/responses/response.py
  • src/openai/types/responses/response_function_web_search.py
  • src/openai/types/responses/response_function_web_search_param.py
  • src/openai/types/responses/responses_client_event.py
  • src/openai/types/responses/responses_client_event_param.py
  • src/openai/types/responses/tool.py
  • src/openai/types/responses/tool_param.py
  • src/openai/types/webhooks/init.py

3 more in the full report.

A changed generated baseline means this report cannot reliably identify which handwritten lines changed.

Inspect the custom-code diff

Download the exact patch produced by this run (requires repository access):

gh run download 34538355473 --repo openai/openai-python \
  --name castiron-custom-code-34538355473-1 --dir /tmp/castiron-custom-code-34538355473-1
git apply --stat /tmp/castiron-custom-code-34538355473-1/custom-code.patch
cat /tmp/castiron-custom-code-34538355473-1/custom-code.patch

Or reproduce it from an SDK checkout containing the vendored reporter:

git fetch --no-tags origin ae41bc46cdd53d17e948ac8a73e16bdbf34123a1 6982b4bb1fc01bddadd7ef8f62f15f5e5d1a691d
python3 scripts/castiron/custom_code_report.py report \
  --base ae41bc46cdd53d17e948ac8a73e16bdbf34123a1 \
  --head 6982b4bb1fc01bddadd7ef8f62f15f5e5d1a691d --fetch --require-head-hash --public \
  --out /tmp/castiron-custom-code-6982b4bb1fc0
cat /tmp/castiron-custom-code-6982b4bb1fc0/custom-code.patch

This is the current full custom patch for mixed files, not an attribution of only the handwritten lines changed by this PR.

Full report and patch

@marcuswood-oai
marcuswood-oai merged commit d7c41ef into openai:main Sep 10, 2026
18 checks passed
@openai-sdks openai-sdks Bot mentioned this pull request Sep 10, 2026
@marcuswood-oai

Copy link
Copy Markdown
Contributor

Thanks for the fix and careful testing! Merged.

@heri-espino

Copy link
Copy Markdown
Contributor Author

Thank you! Glad the fix and tests were useful :)

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

2 participants