aio-libs/aiohttp · error · ClientConnectionError
Connection closed
Error message
Connection closed
What it means
Raised as ClientConnectionError in ClientResponse.read() when the response body has already been released (self._released is True) but read() is called again. After release(), the underlying connection is returned to the pool and the payload stream is gone, so there is nothing left to read.
Source
Thrown at aiohttp/client_reqrep.py:706
async def _on_chunk_response_received(self, chunk: bytes) -> None:
try:
for trace in self._traces:
await trace.send_response_chunk_received(self.method, self.url, chunk)
except BaseException:
self.close()
raise
async def read(self) -> bytes:
"""Read response payload."""
if self._body is None:
try:
self._body = await self.content.read()
except BaseException:
self.close()
raise
elif self._released: # Response explicitly released
raise ClientConnectionError("Connection closed")
protocol = self._connection and self._connection.protocol
if protocol is None or not protocol.upgraded:
await self._wait_released() # Underlying connection released
return self._body
def get_encoding(self) -> str:
ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower()
mimetype = parse_mimetype(ctype)
encoding = mimetype.parameters.get("charset")
if encoding:
with contextlib.suppress(LookupError, ValueError):
return codecs.lookup(encoding).name
if mimetype.type == "application" and (
mimetype.subtype == "json" or mimetype.subtype == "rdap"
):View on GitHub (pinned to c0ef574e29)
Solutions
- Cache the result of the first read(): body = await resp.read() and reuse `body`.
- Avoid calling release() before you finish reading; let the context manager handle it.
- Keep all reads inside the `async with` block.
- If you need the body later, store it in a variable before exiting the context.
Example fix
# before
async with session.get(url) as resp:
pass
data = await resp.read() # response already released
# after
async with session.get(url) as resp:
data = await resp.read()
# use `data` afterwards Defensive patterns
Strategy: try-catch
Validate before calling
if resp._released:
raise RuntimeError('response already released; use cached body') Try / catch
from aiohttp import ClientConnectionError
try:
body = await resp.read()
except ClientConnectionError as e:
if 'Connection closed' in str(e):
# response was released; re-fetch or use cached body Prevention
- Read the body once inside the async with block and cache the bytes.
- Never call release() before finishing reads.
- Treat the response as single-use; design retry code to re-issue the request.
When it happens
Trigger: Fires at line 705-706 when self._body is not None (so the first branch is skipped) but self._released flag is set. Happens when calling response.read()/text()/json() a second time after response.release() was invoked, or after the async context manager exited and released the response.
Common situations: Calling await response.read() after the `async with session.get(...)` block has already exited; mixing manual release() with later reads; retry logic that re-reads a released response.
Related errors
- Cannot compute fallback encoding of a not yet read body
- Attempt to decode JSON with unexpected mimetype: %s
- Cannot write to closing transport
- Session is closed
- Security issue: Digest auth challenge contains empty 'nonce'
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/3f6cda4a5e28a431.json.
Report an issue: GitHub.