aio-libs/aiohttp · error · ClientConnectionResetError

Connection lost

Error message

Connection lost

What it means

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.

Solutions

  1. Catch ClientConnectionResetError / ConnectionError around send and drain operations and move the app to a disconnected state.
  2. Reduce the chance of pausing by keeping individual messages reasonably sized, and await send_frame so backpressure is observed.
  3. Implement a reconnect-with-backoff loop for resilient clients.
  4. Cancel outstanding writers when a connection-lost signal is received.

Example fix

// before
await ws.send_str(huge_payload)   // drain after transport lost -> error
// after
try:
    await ws.send_str(huge_payload)
except (ClientConnectionResetError, ConnectionError):
    await reconnect_with_backoff()
Defensive patterns

Strategy: try-catch

Validate before calling

// Check connection liveness before issuing large/backpressured writes.
if ws._writer.transport is None or ws.closed:
    raise ClientConnectionResetError('no transport')

Type guard

def connection_alive(ws) -> bool:
    t = getattr(ws._writer, 'transport', None)
    return t is not None and not ws.closed

Try / catch

try:
    await ws.send_str(payload)
except (ClientConnectionResetError, ConnectionError):
    await reconnect_with_backoff()

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/5003235d250ece0e. Report an issue: GitHub.

Appendix: source

Thrown at aiohttp/base_protocol.py:133

            return
        waiter = self._drain_waiter
        if waiter is None:
            return
        self._drain_waiter = None
        if waiter.done():
            return
        if exc is None:
            waiter.set_result(None)
        else:
            set_exception(
                waiter,
                ConnectionError("Connection lost"),
                exc,
            )

    async def _drain_helper(self) -> None:
        if self.transport is None:
            raise ClientConnectionResetError("Connection lost")
        if not self._paused:
            return
        waiter = self._drain_waiter
        if waiter is None:
            waiter = self._loop.create_future()
            self._drain_waiter = waiter
        await waiter

View on GitHub (pinned to d041d4d0fd)