{"record":{"id":"3f6cda4a5e28a431","repo":"aio-libs/aiohttp","slug":"connection-closed","errorCode":null,"errorMessage":"Connection closed","messagePattern":"Connection closed","errorType":"exception","errorClass":"ClientConnectionError","httpStatus":null,"severity":"error","filePath":"aiohttp/client_reqrep.py","lineNumber":706,"sourceCode":"\n    async def _on_chunk_response_received(self, chunk: bytes) -> None:\n        try:\n            for trace in self._traces:\n                await trace.send_response_chunk_received(self.method, self.url, chunk)\n        except BaseException:\n            self.close()\n            raise\n\n    async def read(self) -> bytes:\n        \"\"\"Read response payload.\"\"\"\n        if self._body is None:\n            try:\n                self._body = await self.content.read()\n            except BaseException:\n                self.close()\n                raise\n        elif self._released:  # Response explicitly released\n            raise ClientConnectionError(\"Connection closed\")\n\n        protocol = self._connection and self._connection.protocol\n        if protocol is None or not protocol.upgraded:\n            await self._wait_released()  # Underlying connection released\n        return self._body\n\n    def get_encoding(self) -> str:\n        ctype = self.headers.get(hdrs.CONTENT_TYPE, \"\").lower()\n        mimetype = parse_mimetype(ctype)\n\n        encoding = mimetype.parameters.get(\"charset\")\n        if encoding:\n            with contextlib.suppress(LookupError, ValueError):\n                return codecs.lookup(encoding).name\n\n        if mimetype.type == \"application\" and (\n            mimetype.subtype == \"json\" or mimetype.subtype == \"rdap\"\n        ):","sourceCodeStart":688,"sourceCodeEnd":724,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/client_reqrep.py#L688-L724","documentation":"Raised as ClientConnectionError('Connection closed') from ClientResponse.read() when the response has already been explicitly released (_released=True) and you then call read(). After release(), the underlying connection is returned to the pool and the body is no longer accessible, so read() refuses to hand back stale/missing data. This is distinct from a network drop during the initial read.","triggerScenarios":"Calling resp.release() (or the context manager exiting and releasing), then calling resp.read() again on the same ClientResponse object. The _released flag is True and _body was never populated, so read() raises ClientConnectionError.","commonSituations":"Calling read() after the 'async with session.get() as resp' block has exited; calling release() manually then trying to read; storing the response object and using it after its lifecycle ended; mixing streaming (content.read()) with a later full read().","solutions":["Read the body before releasing: call await resp.read() (or .text()/.json()) while still inside the response context.","Cache the bytes you need: body = await resp.read(); then use body later instead of resp.read() again.","Avoid calling resp.release() manually if you intend to read later; let the context manager handle lifecycle.","For streaming, consume resp.content fully before exiting the context."],"exampleFix":"# before\nasync with session.get(url) as resp:\n    pass\nbody = await resp.read()  # ClientConnectionError: Connection closed\n\n# after\nasync with session.get(url) as resp:\n    body = await resp.read()  # read while alive\n# use `body` afterwards","handlingStrategy":"validation","validationCode":"async def read_once(resp):\n    if resp.closed or getattr(resp, '_released', False):\n        raise RuntimeError('response already released; cannot read')\n    return await resp.read()\n\n# cache the body and reuse it instead of re-reading\nasync with session.get(url) as resp:\n    body = await resp.read()\n# use `body` later, not resp.read()","typeGuard":"def response_is_readable(resp) -> bool:\n    return not getattr(resp, '_released', False) and not resp.closed","tryCatchPattern":"from aiohttp import ClientConnectionError\n\ntry:\n    body = await resp.read()\nexcept ClientConnectionError as e:\n    if str(e) == 'Connection closed':\n        # response was released; you must re-issue the request\n        resp = await session.get(url)\n        body = await resp.read()\n    else:\n        raise","preventionTips":["Read the body inside the 'async with' block; store bytes, not the response object.","Never call release() before read().","Treat the response as unusable once the context manager exits."],"tags":["client","response","lifecycle","connection","usage"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}