fix(files): stream content in 1 MB chunks to prevent ConnectionResetError on large Batch API files#2985
Conversation
…rror on large files Fixes openai#2959 Batch API result files larger than ~200 MB caused ConnectionResetError because HttpxBinaryResponseContent.content read the entire HTTP body at once via response.content. Long-lived connections get reset by the server before a single large read completes. Fix: replace response.content with a loop over response.iter_bytes() using 1 MB chunks. The return type and semantics are unchanged — the property still returns bytes — but the data is read incrementally, keeping the connection alive throughout the download.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 09befca722
ℹ️ 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".
| _CHUNK_SIZE = 1024 * 1024 # 1 MB | ||
| buf = bytearray() | ||
| for chunk in self.response.iter_bytes(chunk_size=_CHUNK_SIZE): | ||
| buf.extend(chunk) |
There was a problem hiding this comment.
Chunk the body before
APIResponse.parse() reads it
For the normal client.files.content(...) flow, HttpxBinaryResponseContent is only constructed after APIResponse.parse() has already called self.read() on the httpx.Response (src/openai/_response.py:323-339). Any large-file download failure therefore happens before this property is ever reached, so moving the loop here does not change the code path that actually reads from the socket and will not fix the reported ConnectionResetError for the non-streaming binary-download API.
Useful? React with 👍 / 👎.
| buf = bytearray() | ||
| for chunk in self.response.iter_bytes(chunk_size=_CHUNK_SIZE): | ||
| buf.extend(chunk) | ||
| return bytes(buf) |
There was a problem hiding this comment.
Avoid copying the full binary payload a second time
In the same non-streaming path, the response body has already been buffered by the time this wrapper is created (src/openai/_response.py:323-339), so iter_bytes() here just replays response._content. Rebuilding that into a bytearray and then bytes makes an extra full-size copy of every file; for the 200MB+ downloads mentioned in the commit message, .content now adds hundreds of MB of peak memory and extra CPU where return self.response.content previously reused the existing buffer.
Useful? React with 👍 / 👎.
Summary
Fixes #2959
HttpxBinaryResponseContent.contentread the entire HTTP response body at once viaresponse.content. For large Batch API result files (>~200 MB), this triggered aConnectionResetErrorbecause long-lived server connections were reset before the single large read completed.Changes
Replaced
return self.response.contentwith a chunked loop overresponse.iter_bytes(chunk_size=1 MB). The return type and behaviour are unchanged — the property still returnsbytes— but data is now read incrementally, keeping the connection alive.Before
After
Testing
Users downloading Batch API result files with
client.files.content(file_id).contentshould no longer seeConnectionResetErrorfor large files.