{"record":{"id":"658bd2d41ab8aa10","repo":"redis/redis-py","slug":"connection-closed-by-server-658bd2","errorCode":null,"errorMessage":"Connection closed by server.","messagePattern":"Connection closed by server\\.","errorType":"exception","errorClass":"ConnectionError","httpStatus":null,"severity":"error","filePath":"redis/_parsers/resp2.py","lineNumber":34,"sourceCode":"        pos = self._buffer.get_pos() if self._buffer else None\n        try:\n            result = self._read_response(\n                disable_decoding=disable_decoding, timeout=timeout\n            )\n        except BaseException:\n            if self._buffer:\n                self._buffer.rewind(pos)\n            raise\n        else:\n            self._buffer.purge()\n            return result\n\n    def _read_response(\n        self, disable_decoding=False, timeout: Union[float, object] = SENTINEL\n    ):\n        raw = self._buffer.readline(timeout=timeout)\n        if not raw:\n            raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)\n\n        byte, response = raw[:1], raw[1:]\n\n        # server returned an error\n        if byte == b\"-\":\n            response = response.decode(\"utf-8\", errors=\"replace\")\n            error = self.parse_error(response)\n            # if the error is a ConnectionError, raise immediately so the user\n            # is notified\n            if isinstance(error, ConnectionError):\n                raise error\n            # otherwise, we're dealing with a ResponseError that might belong\n            # inside a pipeline response. the connection's read_response()\n            # and/or the pipeline's execute() will raise this error if\n            # necessary, so just return the exception instance here.\n            return error\n        # single value\n        elif byte == b\"+\":","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/_parsers/resp2.py#L16-L52","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Configure retry_on_error=[ConnectionError, TimeoutError] + Retry/backoff.","Enable health_check_interval and socket_keepalive.","Raise the redis 'timeout' to exceed your longest blocking command.","Catch redis.exceptions.ConnectionError and reconnect/retry."],"exampleFix":"# before\nr = redis.Redis(protocol=2)  # pure-Python RESP2 path\nr.get('k')  # after server restart -> ConnectionError: Connection closed by server.\n\n# after\nfrom redis.retry import Retry\nfrom redis.backoff import ExponentialWithJitterBackoff\nfrom redis.exceptions import ConnectionError, TimeoutError\nr = redis.Redis(\n    protocol=2, health_check_interval=30, socket_keepalive=True,\n    retry_on_error=[ConnectionError, TimeoutError],\n    retry=Retry(ExponentialWithJitterBackoff(), 3),\n)\nr.get('k')","handlingStrategy":"retry","validationCode":"# Liveness probe before critical work (pure-Python RESP2 client)\nfrom redis.exceptions import ConnectionError\n\ndef alive(r):\n    try:\n        r.ping()\n        return True\n    except ConnectionError:\n        return False","typeGuard":"from redis.exceptions import ConnectionError as RCE\n\ndef resp2_server_closed(e: BaseException) -> bool:\n    return isinstance(e, RCE) and 'closed by server' in str(e).lower()","tryCatchPattern":"from redis.exceptions import ConnectionError, TimeoutError\ntry:\n    r.get('k')\nexcept (ConnectionError, TimeoutError):\n    r.get('k')","preventionTips":["redis.exceptions.ConnectionError does not subclass the builtin - catch the redis class.","Health checks (health_check_interval > 0) catch stale connections before your command does."],"tags":["network","connection","sync","resp2","server-closed"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}