redis/redis-py · error · ConnectionError

Connection closed by server.

Error message

Connection closed by server.

What it means

Raised by the async RESP parser's on_connect() when connection._reader (the asyncio.StreamReader) is None at the moment the parser is wired up. The parser cannot read any bytes without a stream, so it refuses to connect rather than fail later with an opaque AttributeError. It is a ConnectionError (error_type=NETWORK) so existing retry/backoff logic treats it as retryable.

Source

Thrown at redis/_parsers/base.py:528

    __slots__ = AsyncBaseParser.__slots__ + ("encoder", "_buffer", "_pos", "_chunks")

    def __init__(self, socket_read_size: int):
        super().__init__(socket_read_size)
        self.encoder: Optional[Encoder] = None
        self._buffer = b""
        self._chunks = []
        self._pos = 0

    def _clear(self):
        self._buffer = b""
        self._chunks.clear()

    def on_connect(self, connection):
        """Called when the stream connects"""
        self._stream = connection._reader
        if self._stream is None:
            raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)
        self.encoder = connection.encoder
        self._clear()
        self._connected = True

    def on_disconnect(self):
        """Called when the stream disconnects"""
        self._connected = False

    @deprecated_function(
        version="8.0.0",
        reason="Use can_read() instead",
        name="can_read_destructive",
    )
    async def can_read_destructive(self) -> bool:
        return await self.can_read()

    async def can_read(self) -> bool:
        # TODO: Rename this API; it detects pending data or dirty/closed

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Ensure the asyncio event loop running the client is the same one that created it and stays alive for the connection's lifetime.
  2. Do not share or fork Redis async connections across event loops; create a fresh redis.asyncio.Redis after fork/loop restart.
  3. If using SSL/TLS, verify the certificate and endpoint are reachable so the handshake completes and a transport is installed.
  4. Wrap connect in a retry (redis sets retry_on_error=[ConnectionError] / retry strategy) to re-establish a clean transport.

Example fix

// before
r = redis.asyncio.Redis(host=..., port=...)
await r.get("k")  # loop was closed/replaced since construction

// after
r = redis.asyncio.Redis(host=..., port=..., retry=Retry(ExponentialBackoff(), 3),
                       retry_on_error=[redis.exceptions.ConnectionError])
await r.get("k")
Defensive patterns

Strategy: retry

Validate before calling

# Verify an event loop is running and the connection can open before first use
import asyncio, redis.asyncio as redis
assert asyncio.get_running_loop() is not None, "call inside an async context"
async def health(r):
    try:
        await r.ping()
        return True
    except (redis.ConnectionError, OSError):
        return False

Try / catch

try:
    await r.ping()
except redis.exceptions.ConnectionError as e:
    if "Connection closed by server" in str(e):
        # transport not installed; recreate client on the current loop
        await r.close()
        r = redis.asyncio.Redis(...)
        await r.ping()

Prevention

When it happens

Trigger: Triggered when an asyncio Redis connection initializes its parser before the StreamReader is set on the connection - e.g. the transport was closed/dropped between socket creation and parser.on_connect(), or a manually constructed Connection object was used without a live transport. Most commonly surfaces inside redis.asyncio.Connection.connect() if the transport callback fired with an error.

Common situations: Event loop being closed/replaced while a connection is mid-handshake; using redis.asyncio inside a forked process without reconnecting; SSL handshake failure that tears down the transport before on_connect runs; test fixtures that mock the connection but leave _reader unset.

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/5a0704eed488ab67.json. Report an issue: GitHub.