redis/redis-py · error · ConnectionError
Connection closed by server.
Error message
Connection closed by server.
What it means
Raised in _HiredisParser.can_read() (redis/_parsers/hiredis.py:163) when self._reader is falsy — i.e., on_disconnect() has already cleared the hiredis reader (set it to None) but can_read() is still called. The parser has been torn down, so no readiness check is possible; it raises ConnectionError('Connection closed by server.').
Solutions
- Catch ConnectionError from can_read() and treat it as 'discard and reconnect'.
- Track liveness externally and stop probing once disconnected.
- Let the connection pool hand out a fresh connection instead of probing a dead one.
- Avoid closing a shared client from one thread while another inspects it.
Example fix
# before
if conn.can_read(): # raises on a disconnected hiredis conn
...
# after
try:
ready = conn.can_read()
except redis.ConnectionError:
conn = pool.get_connection()
ready = False Defensive patterns
Strategy: try-catch
Validate before calling
# Avoid probing a parser whose reader is already torn down
if not getattr(parser, "_reader", None):
# reader cleared by on_disconnect(); reconnect instead of probing
conn = pool.get_connection() Try / catch
try:
ready = conn.can_read()
except redis.ConnectionError:
# hiredis reader is None (disconnected) -> discard and reconnect
conn.disconnect()
conn = pool.get_connection() Prevention
- Track connection liveness and stop probing once disconnected.
- Let the connection pool hand out fresh connections rather than reusing dead ones.
- Avoid closing a shared client from another thread while a probe is in flight.
When it happens
Trigger: Probing a sync hiredis-backed connection's readability after it was disconnected: pubsub get_message loops, connection-pool liveness checks, or failover probes that touch a connection whose on_disconnect() already ran.
Common situations: A pubsub/health loop continuing after the server dropped the connection; reusing a connection after explicit disconnect; a shared client closed via 'with redis:' while another thread probes it.
Understand the failure class
- Connection failures: ECONNREFUSED, ECONNRESET, and friends — why connections get refused, reset, or dropped.
Related errors
- Bad response from PING health check
- Buffer is closed.
- Buffer is closed.
- Connection closed by server.
- Connection closed by server.
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/a1b09dc13c03d8d8.
Report an issue: GitHub.
Appendix: source
Thrown at redis/_parsers/hiredis.py:163
if connection.encoder.decode_responses:
kwargs["encoding"] = connection.encoder.encoding
self._reader = hiredis.Reader(**kwargs)
try:
self._hiredis_PushNotificationType = hiredis.PushNotification
except AttributeError:
# hiredis < 3.2
self._hiredis_PushNotificationType = None
def on_disconnect(self):
self._sock = None
self._reader = None
def can_read(self, timeout: float = 0) -> bool:
# TODO: Rename this API; it detects pending data or dirty/closed
# connection state, not only whether application data can be read.
if not self._reader:
raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)
if self._reader.has_data():
return True
if not _socket_can_read(self._sock, timeout):
return False
# the socket reports readable but the reader has no buffered data. a
# server-closed socket also reads as ready (it yields EOF), so tell the
# two apart with a non-destructive poll: a peer-closed socket must not be
# reused, while a readable-but-open socket may just hold a pending push.
# this mirrors how the pure-Python parser (recv -> b"") and the async
# parser (StreamReader.at_eof()) already signal a closed connection.
if _socket_is_closed(self._sock):
raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)
return True
def read_from_socket(self, timeout=SENTINEL, raise_on_timeout=True):
sock = self._sock
reader = self._readerView on GitHub (pinned to 6a6b581b48)