{"id":"8eec40b015078dca","repo":"aio-libs/aiohttp","slug":"connection-lost-8eec40","errorCode":null,"errorMessage":"Connection lost","messagePattern":"Connection lost","errorType":"exception","errorClass":"ConnectionResetError","httpStatus":null,"severity":"warning","filePath":"aiohttp/web_fileresponse.py","lineNumber":137,"sourceCode":"                break\n            chunk = await loop.run_in_executor(None, fobj.read, min(chunk_size, count))\n\n        await writer.drain()\n        return writer\n\n    async def _sendfile(\n        self, request: \"BaseRequest\", fobj: BinaryIO, offset: int, count: int\n    ) -> AbstractStreamWriter:\n        writer = await super().prepare(request)\n        assert writer is not None\n\n        if NOSENDFILE or self.compression:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        loop = request._loop\n        transport = request.transport\n        if transport is None:\n            raise ConnectionResetError(\"Connection lost\")\n\n        try:\n            if aiofastnet is not None:\n                await aiofastnet.sendfile(loop, transport, fobj, offset, count)\n            else:\n                await loop.sendfile(transport, fobj, offset, count)  # type: ignore[unreachable]\n        except NotImplementedError:\n            return await self._sendfile_fallback(writer, fobj, offset, count)\n\n        await super().write_eof()\n        return writer\n\n    @staticmethod\n    def _etag_match(etag_value: str, etags: tuple[ETag, ...], *, weak: bool) -> bool:\n        if len(etags) == 1 and etags[0].value == ETAG_ANY:\n            return True\n        return any(\n            etag.value == etag_value for etag in etags if weak or not etag.is_weak","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/web_fileresponse.py#L119-L155","documentation":"Raised as ConnectionResetError('Connection lost') in FileResponse._sendfile (aiohttp/web_fileresponse.py:137) when request.transport is None at the moment aiohttp is about to call loop.sendfile / aiofastnet.sendfile. A None transport means the underlying socket has already been closed (client disconnected or the protocol's transport was replaced) and there is no longer a file descriptor to send to. This is raised before any bytes are sent via sendfile(2).","triggerScenarios":"Serving a static file with web.FileResponse(path) after the client has closed the connection (e.g. user aborted the download, or a proxy timed out and tore down the TCP connection). The handler still ran to the point of preparing the response, but transport was reset to None by the time _sendfile looked at it.","commonSituations":"Mobile clients on flaky networks, browsers cancelling downloads, or downstream proxies (nginx, CloudFront) with aggressive client-header-buffer / read timeouts. Also reproducible in tests that close the test client before the handler reaches sendfile.","solutions":["Treat ConnectionResetError / ConnectionError from FileResponse as expected and log at debug, not error - the client is already gone.","Wrap file-serving handlers in try/except ConnectionError and return early.","Tune upstream proxy timeouts (client_body_timeout, send_timeout) if disconnects happen mid-transfer on healthy clients.","Verify the file path is valid before constructing FileResponse so unrelated failures don't masquerade as transport loss."],"exampleFix":"// before\nasync def serve(request):\n    return web.FileResponse(path)\n\n# after\nasync def serve(request):\n    try:\n        return web.FileResponse(path)\n    except (ConnectionResetError, ConnectionError):\n        logger.debug(\"client gone during FileResponse(%s)\", path)\n        raise","handlingStrategy":"try-catch","validationCode":"if request.transport is None:\n    raise web.HTTPUnavailable()  # client already gone","typeGuard":"def transport_alive(request) -> bool:\n    return request.transport is not None","tryCatchPattern":"try:\n    return web.FileResponse(path)\nexcept (ConnectionResetError, ConnectionError):\n    logger.debug(\"client gone during FileResponse\")\n    raise","preventionTips":["Treat ConnectionError from FileResponse as expected; log at debug, not error.","Tune upstream proxy timeouts if disconnects are frequent on healthy clients.","Validate file paths before construction so unrelated errors are not masked."],"tags":["network","connection-reset","sendfile","file-response"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}