Skip to content

docs: clarify async client cleanup - #2388

Merged
marcuswood-oai merged 3 commits into
openai:mainfrom
damccorm:patch-1
Sep 15, 2026
Merged

marcuswood-oai merged 3 commits into
openai:mainfrom
damccorm:patch-1

Conversation

@damccorm

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

Changes being requested

When using multiple AsyncOpenAI clients to send records to a local vLLM server, we ran into an issue with garbage collection after switching from the sync client to the async client. We started seeing a bunch of errors like:

Traceback (most recent call last):\n  File \"/usr/local/lib/python3.9/dist-packages/httpx/_client.py\", line 1985, in aclose\n    await self._transport.aclose()\n  File \"/usr/local/lib/python3.9/dist-packages/httpx/_transports/default.py\", line 406, in aclose\n    await self._pool.aclose()\n  File \"/usr/local/lib/python3.9/dist-packages/httpcore/_async/connection_pool.py\", line 353, in aclose\n    await self._close_connections(closing_connections)\n  File \"/usr/local/lib/python3.9/dist-packages/httpcore/_async/connection_pool.py\", line 345, in _close_connections\n    await connection.aclose()\n  File \"/usr/local/lib/python3.9/dist-packages/httpcore/_async/connection.py\", line 173, in aclose\n    await self._connection.aclose()\n  File \"/usr/local/lib/python3.9/dist-packages/httpcore/_async/http11.py\", line 258, in aclose\n    await self._network_stream.aclose()\n  File \"/usr/local/lib/python3.9/dist-packages/httpcore/_backends/anyio.py\", line 53, in aclose\n    await self._stream.aclose()\n  File \"/usr/local/lib/python3.9/dist-packages/anyio/_backends/_asyncio.py\", line 1314, in aclose\n    self._transport.close()\n  File \"/usr/lib/python3.9/asyncio/selector_events.py\", line 698, in close\n    self._loop.call_soon(self._call_connection_lost, None)\n  File \"/usr/lib/python3.9/asyncio/base_events.py\", line 751, in call_soon\n    self._check_closed()\n  File \"/usr/lib/python3.9/asyncio/base_events.py\", line 515, in _check_closed\n    raise RuntimeError('Event loop is closed')\nRuntimeError: Event loop is closed\n

which indicated that the event loop was being closed before we could close the client. Concurrently, we saw degraded performance. When we switched to with blocks for resource management, the problem went away (PR - https://github.com/apache/beam/pull/35053/files). I seems like relying on normal garbage collection calling https://github.com/openai/openai-python/blob/f588695f77aad9279a355f5f483d8debf92b46ed/src/openai/_base_client.py#L1318C21-L1318C37 can cause a meaningful performance issue because the event loop can be destroyed before the client cleanup finishes.

Its not clear to me if the slowness was caused by:

  1. Overhead associated with trying to call a closed event loop
  2. Actually failing to close the connections

Regardless, it would be nice to doc this since switching clients did not work without some minor changes.

@damccorm
damccorm requested a review from a team as a code owner May 30, 2025 13:29
@RobertCraigie RobertCraigie added documentation Improvements or additions to documentation sdk labels Jul 10, 2025
@marcuswood-oai marcuswood-oai changed the title Add info on garbage collection of async clients docs: clarify async client cleanup Sep 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Castiron custom code

✅ No new custom-code files detected.

43 mixed files remain; 0 existing customizations changed.

Compared 1493321affb1 → 709862d746a1. 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 35005411841 --repo openai/openai-python \
  --name castiron-custom-code-35005411841-1 --dir /tmp/castiron-custom-code-35005411841-1
git apply --stat /tmp/castiron-custom-code-35005411841-1/custom-code.patch
cat /tmp/castiron-custom-code-35005411841-1/custom-code.patch

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

git fetch --no-tags origin 1493321affb1814fba7b669892e376a119d8d36f 709862d746a1f8c7e24cc2e116fc5a03026bb5db
python3 scripts/castiron/custom_code_report.py report \
  --base 1493321affb1814fba7b669892e376a119d8d36f \
  --head 709862d746a1f8c7e24cc2e116fc5a03026bb5db --fetch --require-head-hash --public \
  --out /tmp/castiron-custom-code-709862d746a1
cat /tmp/castiron-custom-code-709862d746a1/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

Copy link
Copy Markdown
Contributor

Thanks for documenting the cleanup issue you encountered when switching to the async client. AsyncOpenAI awaits cleanup when its async context exits, while garbage collection cannot reliably finish that work after the event loop shuts down. This is useful guidance for managing the existing client lifecycle.

@marcuswood-oai
marcuswood-oai added this pull request to the merge queue Sep 15, 2026
Merged via the queue into openai:main with commit a3d83b5 Sep 15, 2026
16 checks passed
@openai-sdks openai-sdks Bot mentioned this pull request Sep 15, 2026
pull Bot pushed a commit to Pyrex77/openai-python that referenced this pull request Sep 15, 2026
Automated Release PR
---


##
[3.14.1](openai/openai-python@v3.14.0...v3.14.1)
(2026-09-15)


### Bug Fixes

* **client:** validate retry limits and preserve application errors
([openai#3867](openai#3867))
([f86c721](openai@f86c721))
* correct typo "th" to "the" in StreamAlreadyConsumed error message
([openai#3022](openai#3022))
([7186203](openai@7186203))
* **examples:** correct Azure endpoint hostname
([openai#3298](openai#3298))
([543516c](openai@543516c))
* **examples:** refresh chat streaming examples
([openai#2974](openai#2974))
([54f460e](openai@54f460e))
* **examples:** split push-to-talk shebang arguments
([openai#3210](openai#3210))
([d241ed6](openai@d241ed6))
* **examples:** use uv in remaining Python launchers
([openai#3821](openai#3821))
([8c76c4f](openai@8c76c4f))
* log only metadata for Live WebSocket diagnostics
([openai#3865](openai#3865))
([ccc10f5](openai@ccc10f5))
* **responses:** skip structured parsing for commentary
([openai#3861](openai#3861))
([6520df1](openai@6520df1))


### Chores

* **examples:** bump module_client.py to gpt-5.6
([openai#3211](openai#3211))
([1493321](openai@1493321))
* remove unused fine-tuning data validators
([openai#3863](openai#3863))
([9640f29](openai@9640f29))


### Documentation

* clarify async client cleanup
([openai#2388](openai#2388))
([a3d83b5](openai@a3d83b5))
* clarify filenames for in-memory file uploads
([openai#3729](openai#3729))
([cb27380](openai@cb27380))
* **client:** correct retry jitter comment
([openai#2252](openai#2252))
([bdbbc16](openai@bdbbc16))
* fix Realtime docstring wording
([openai#3535](openai#3535))
([8cb9ded](openai@8cb9ded))
* fix reusing wording in Azure client docstrings
([openai#3061](openai#3061))
([dc625f9](openai@dc625f9))
* fix streaming comment typo
([openai#3005](openai#3005))
([d421d7a](openai@d421d7a))
* fix vector store polling helper paths
([openai#3101](openai#3101))
([2ef2de6](openai@2ef2de6))
* **readme:** correct SSE terminology
([openai#2837](openai#2837))
([caaa9b4](openai@caaa9b4))
* update Responses API reference link
([openai#3279](openai#3279))
([557bd06](openai@557bd06))
* **utils:** correct query component RFC reference
([openai#3300](openai#3300))
([929a8a5](openai@929a8a5))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: openai-sdks[bot] <284451331+openai-sdks[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation sdk

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants