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/closedView on GitHub (pinned to da03cdc7e8)
Solutions
- Ensure the asyncio event loop running the client is the same one that created it and stays alive for the connection's lifetime.
- Do not share or fork Redis async connections across event loops; create a fresh redis.asyncio.Redis after fork/loop restart.
- If using SSL/TLS, verify the certificate and endpoint are reachable so the handshake completes and a transport is installed.
- 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
- Create redis.asyncio.Redis inside the async context that will use it, never before the loop starts.
- Do not reuse an async client across event loops or after loop restart; reconstruct it.
- Verify TLS endpoints are reachable so the transport handshake installs a reader.
- In forked children, discard the parent's async client and create a new one.
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
- Timed out closing connection after {self.socket_connect_time
- Buffer is closed.
- Maintenance notifications are only supported with hiredis an
- Error setting client name
- Invalid Database
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/5a0704eed488ab67.json.
Report an issue: GitHub.