{"record":{"id":"5003235d250ece0e","repo":"aio-libs/aiohttp","slug":"connection-lost","errorCode":null,"errorMessage":"Connection lost","messagePattern":"Connection lost","errorType":"exception","errorClass":"ClientConnectionResetError","httpStatus":null,"severity":"error","filePath":"aiohttp/base_protocol.py","lineNumber":133,"sourceCode":"            return\n        waiter = self._drain_waiter\n        if waiter is None:\n            return\n        self._drain_waiter = None\n        if waiter.done():\n            return\n        if exc is None:\n            waiter.set_result(None)\n        else:\n            set_exception(\n                waiter,\n                ConnectionError(\"Connection lost\"),\n                exc,\n            )\n\n    async def _drain_helper(self) -> None:\n        if self.transport is None:\n            raise ClientConnectionResetError(\"Connection lost\")\n        if not self._paused:\n            return\n        waiter = self._drain_waiter\n        if waiter is None:\n            waiter = self._loop.create_future()\n            self._drain_waiter = waiter\n        await waiter\n","sourceCodeStart":115,"sourceCodeEnd":141,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/base_protocol.py#L115-L141","documentation":"Raised as ClientConnectionResetError('Connection lost') in BaseProtocol._drain_helper when self.transport is None — i.e. connection_lost() has already run and nulled the transport, but a drain (flow-control wait) is still attempted. (The companion path at line 127 sets ConnectionError('Connection lost') on a pending drain waiter when connection_lost receives an exception.) This signals the underlying connection is gone and buffered writes cannot be flushed.","triggerScenarios":"send_frame triggers flow-control drain (self._output_size > self._limit and protocol._paused), and _drain_helper is called after connection_lost already set self.transport = None. Equivalently, a drain waiter is resolved with ConnectionError when connection_lost receives an exc.","commonSituations":"The peer disconnects while aiohttp is paused on flow control and then tries to drain; large uploads where the transport closes mid-stream; network interruptions / RST during heavy traffic; server shutting down while client writes; the writer's output buffer exceeded the limit exactly as the connection dropped.","solutions":["Catch ClientConnectionResetError / ConnectionError around send and drain operations and move the app to a disconnected state.","Reduce the chance of pausing by keeping individual messages reasonably sized, and await send_frame so backpressure is observed.","Implement a reconnect-with-backoff loop for resilient clients.","Cancel outstanding writers when a connection-lost signal is received."],"exampleFix":"// before\nawait ws.send_str(huge_payload)   // drain after transport lost -> error\n// after\ntry:\n    await ws.send_str(huge_payload)\nexcept (ClientConnectionResetError, ConnectionError):\n    await reconnect_with_backoff()","handlingStrategy":"try-catch","validationCode":"// Check connection liveness before issuing large/backpressured writes.\nif ws._writer.transport is None or ws.closed:\n    raise ClientConnectionResetError('no transport')\n","typeGuard":"def connection_alive(ws) -> bool:\n    t = getattr(ws._writer, 'transport', None)\n    return t is not None and not ws.closed\n","tryCatchPattern":"try:\n    await ws.send_str(payload)\nexcept (ClientConnectionResetError, ConnectionError):\n    await reconnect_with_backoff()\n","preventionTips":["Implement reconnect-with-backoff for resilient clients.","Cancel outstanding writers on connection-lost signals.","Keep individual writes reasonably sized to reduce drain pressure."],"tags":["network","connection-reset","flow-control","drain","transport"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}