redis/redis-py · error · ConnectionError

Connection closed by server.

Error message

Connection closed by server.

What it means

Raised by the sync pure-Python RESP2 parser _read_response when SocketBuffer.readline() returns empty: the buffer hit EOF because the server closed the socket (the underlying recv in SocketBuffer._read_from_socket returned b'', error 36). This is the RESP2 (protocol=2) path's 'Connection closed by server.' and the pure-Python equivalent of the hiredis error 20.

Solutions

  1. Configure retry_on_error=[ConnectionError, TimeoutError] + Retry/backoff.
  2. Enable health_check_interval and socket_keepalive.
  3. Raise the redis 'timeout' to exceed your longest blocking command.
  4. Catch redis.exceptions.ConnectionError and reconnect/retry.

Example fix

# before
r = redis.Redis(protocol=2)  # pure-Python RESP2 path
r.get('k')  # after server restart -> ConnectionError: Connection closed by server.

# after
from redis.retry import Retry
from redis.backoff import ExponentialWithJitterBackoff
from redis.exceptions import ConnectionError, TimeoutError
r = redis.Redis(
    protocol=2, health_check_interval=30, socket_keepalive=True,
    retry_on_error=[ConnectionError, TimeoutError],
    retry=Retry(ExponentialWithJitterBackoff(), 3),
)
r.get('k')
Defensive patterns

Strategy: retry

Validate before calling

# Liveness probe before critical work (pure-Python RESP2 client)
from redis.exceptions import ConnectionError

def alive(r):
    try:
        r.ping()
        return True
    except ConnectionError:
        return False

Type guard

from redis.exceptions import ConnectionError as RCE

def resp2_server_closed(e: BaseException) -> bool:
    return isinstance(e, RCE) and 'closed by server' in str(e).lower()

Try / catch

from redis.exceptions import ConnectionError, TimeoutError
try:
    r.get('k')
except (ConnectionError, TimeoutError):
    r.get('k')

Prevention

When it happens

Trigger: Any sync command over a protocol=2 connection whose reply is interrupted by a server-side close: restart, failover, CLIENT KILL, idle timeout, eviction, network drop. The readline loop (socket.py:112-118) gets b'' and propagates EOF up as an empty 'raw' at resp2.py:33-34.

Common situations: Forced protocol=2 (or an older default) on a long-lived connection behind an aggressive idle reaper; Redis failover; RDB save under memory pressure causing the server to drop clients; a misconfigured 'timeout' in redis.conf.

Understand the failure class

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/658bd2d41ab8aa10. Report an issue: GitHub.

Appendix: source

Thrown at redis/_parsers/resp2.py:34

        pos = self._buffer.get_pos() if self._buffer else None
        try:
            result = self._read_response(
                disable_decoding=disable_decoding, timeout=timeout
            )
        except BaseException:
            if self._buffer:
                self._buffer.rewind(pos)
            raise
        else:
            self._buffer.purge()
            return result

    def _read_response(
        self, disable_decoding=False, timeout: Union[float, object] = SENTINEL
    ):
        raw = self._buffer.readline(timeout=timeout)
        if not raw:
            raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)

        byte, response = raw[:1], raw[1:]

        # server returned an error
        if byte == b"-":
            response = response.decode("utf-8", errors="replace")
            error = self.parse_error(response)
            # if the error is a ConnectionError, raise immediately so the user
            # is notified
            if isinstance(error, ConnectionError):
                raise error
            # otherwise, we're dealing with a ResponseError that might belong
            # inside a pipeline response. the connection's read_response()
            # and/or the pipeline's execute() will raise this error if
            # necessary, so just return the exception instance here.
            return error
        # single value
        elif byte == b"+":

View on GitHub (pinned to 6a6b581b48)