The Fix
Upgrade to version 0.17.0 or later.
Based on closed encode/httpx issue #1463 · PR/commit linked
Production note: Watch p95/p99 latency and retry volume; timeouts can turn into retry storms and duplicate side-effects.
@@ -1500,7 +1500,8 @@ async def _send_single_request(
response.elapsed = datetime.timedelta(seconds=await timer.async_elapsed())
if hasattr(stream, "aclose"):
- await stream.aclose()
+ with map_exceptions(HTTPCORE_EXC_MAP, request=request):
+ await stream.aclose()
import _socket
import asyncio
import io
import struct
import httpx
import pytest
@pytest.mark.asyncio
async def test_download_stream_connection_reset():
async def respond_then_die(reader, writer):
# Read request -- as we should
await reader.readuntil(b"\r\n\r\n")
# Send HTTP header and start sending data -- so the client is streaming
writer.write(
b"HTTP/1.1 200 OK\r\nContent-Length:1000\r\nContent-Type:text/plain\r\n\r\nstart"
)
await writer.drain()
# Now force a TCP RST packet, which will cause response.aclose() to fail
# http://deepix.github.io/2016/10/21/tcprst.html
fd = writer._transport._sock.fileno()
linger = struct.pack("=II", 1, 0)
_socket.socket(fileno=fd).setsockopt(
_socket.SOL_SOCKET, _socket.SO_LINGER, bytearray(linger)
)
writer.close()
server = await asyncio.start_server(respond_then_die, "127.0.0.1")
async with server:
host, port = server.sockets[0].getsockname()
async with httpx.AsyncClient() as client:
async with client.stream("GET", f"http://{host}:{port}") as response:
async for blob in response.aiter_raw():
pass
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
Option A — Upgrade to fixed release\nUpgrade to version 0.17.0 or later.\nWhen NOT to use: This fix should not be used if the application relies on httpcore exceptions for error handling.\n\n
Why This Fix Works in Production
- Trigger: `response.aclose()` raises httpcore errors, not HTTPError
- Mechanism: Maps exceptions in the request.aclose() method, allowing clients to catch exceptions that occur during close.
- Why the fix works: Maps exceptions in the request.aclose() method, allowing clients to catch exceptions that occur during close. (first fixed release: 0.17.0).
Why This Breaks in Prod
- Shows up under Python 3.8 in real deployments (not just unit tests).
- Surfaces as: `response.aclose()` raises httpcore errors, not HTTPError
Proof / Evidence
- GitHub issue: #1463
- Fix PR: https://github.com/encode/httpx/pull/1465
- First fixed release: 0.17.0
- Reproduced locally: No (not executed)
- Last verified: 2026-02-08
- Confidence: 0.85
- Did this fix it?: Yes (upstream fix exists)
- Own content ratio: 0.45
Discussion
High-signal excerpts from the issue thread (symptoms, repros, edge-cases).
“@florimondmanca async with stream() does close the response correctly. The issue I'm highlighting here is only that the wrong exception is raised.”
“Just popping by to say — if async with stream(...) doesn't close the response if there's a´ exception raised while streaming, then I think that…”
Failure Signature (Search String)
- `response.aclose()` raises httpcore errors, not HTTPError
Error Message
Stack trace
Error Message
-------------
`response.aclose()` raises httpcore errors, not HTTPError
Minimal Reproduction
import _socket
import asyncio
import io
import struct
import httpx
import pytest
@pytest.mark.asyncio
async def test_download_stream_connection_reset():
async def respond_then_die(reader, writer):
# Read request -- as we should
await reader.readuntil(b"\r\n\r\n")
# Send HTTP header and start sending data -- so the client is streaming
writer.write(
b"HTTP/1.1 200 OK\r\nContent-Length:1000\r\nContent-Type:text/plain\r\n\r\nstart"
)
await writer.drain()
# Now force a TCP RST packet, which will cause response.aclose() to fail
# http://deepix.github.io/2016/10/21/tcprst.html
fd = writer._transport._sock.fileno()
linger = struct.pack("=II", 1, 0)
_socket.socket(fileno=fd).setsockopt(
_socket.SOL_SOCKET, _socket.SO_LINGER, bytearray(linger)
)
writer.close()
server = await asyncio.start_server(respond_then_die, "127.0.0.1")
async with server:
host, port = server.sockets[0].getsockname()
async with httpx.AsyncClient() as client:
async with client.stream("GET", f"http://{host}:{port}") as response:
async for blob in response.aiter_raw():
pass
Environment
- Python: 3.8
What Broke
Clients experienced unexpected httpcore errors instead of HTTPError during response closure.
Fix Options (Details)
Option A — Upgrade to fixed release Safe default (recommended)
Upgrade to version 0.17.0 or later.
Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.
Fix reference: https://github.com/encode/httpx/pull/1465
First fixed release: 0.17.0
Last verified: 2026-02-08. Validate in your environment.
When NOT to Use This Fix
- This fix should not be used if the application relies on httpcore exceptions for error handling.
Verify Fix
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
Did This Fix Work in Your Case?
Quick signal helps us prioritize which fixes to verify and improve.
Prevention
- Add a CI check that diffs key outputs after upgrades (OpenAPI schema snapshots, JSON payload shapes, CLI output).
- Upgrade behind a canary and run integration tests against the canary before 100% rollout.
- Add a TLS smoke test that performs a real handshake in CI (include CA bundle validation and hostname checks).
- Alert on handshake failures by error string and endpoint to catch cert/CA changes quickly.
Version Compatibility Table
| Version | Status |
|---|---|
| 0.17.0 | Fixed |
Related Issues
No related fixes found.
Sources
We don’t republish the full GitHub discussion text. Use the links above for context.