Skip to content

Close request body async iterator when a write error occurs - #1119

Open
Dextheking1 wants to merge 5 commits into
encode:masterfrom
Dextheking1:fix/3597-writeerror-stream-aclose
Open

Dextheking1 wants to merge 5 commits into
encode:masterfrom
Dextheking1:fix/3597-writeerror-stream-aclose

Conversation

@Dextheking1

@Dextheking1 Dextheking1 commented Sep 23, 2026 •

Copy link
Copy Markdown

Closes encode/httpx#3597.

What

AsyncHTTP11Connection._send_request_body now closes the request body's async iterator in a finally block, so it is cleaned up on both the success and failure paths.

Why

When a WriteError is raised part-way through sending the request body, handle_async_request catches and suppresses it (to allow reading a pre-emptive error response from the server), but the request body's async iterator was left abandoned mid-iteration. It was then garbage collected without ever being exhausted, producing:

ResourceWarning: Async generator '...' was garbage collected before it had been exhausted.
Surround its use in 'async with aclosing(...):' to ensure that it gets cleaned up as soon as you're done using it.

Note: closing the iterator (rather than calling request.stream.aclose()) is what fixes this, e.g. httpx's ByteStream.aclose() is a no-op, while the abandoned object is the generator created by stream.__aiter__(). The aclose call is getattr-guarded so streams whose iterators lack aclose keep working.

Test evidence

New regression test test_http11_write_error_closes_request_body in tests/_async/test_http11.py: a network stream that raises WriteError mid-body, asserting the body iterator receives GeneratorExit while the WriteError stays suppressed and the response remains readable.

Before (on master, trio backend):

ResourceWarning: Async generator '...TrackingBody.__aiter__.<locals>.gen' was garbage collected before it had been exhausted. ...

After: no warning; tests/_async/test_http11.py, 28 passed (asyncio + trio backends). ruff check and ruff format clean.

Dextheking1 and others added 5 commits September 23, 2026 14:09
If a `WriteError` is raised part-way through `_send_request_body`,
the request body's async iterator was abandoned mid-iteration and
later garbage collected without ever being exhausted, triggering
`ResourceWarning: async generator was garbage collected before it
had been exhausted` (encode/httpx#3597).

Close the iterator in a `finally` block so it is cleaned up on both
success and failure paths.
The _sync/http11.py and _sync/test_http11.py files are generated from
their _async counterparts by scripts/unasync.py (enforced by
'scripts/check' in CI). Also annotate the new test's generator so mypy
is clean.
@Dextheking1
Dextheking1 marked this pull request as ready for review September 23, 2026 23:54

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

ResourceWarning in test_write_timeout due to Unclosed Async Generator

1 participant