{"record":{"id":"e52ab68fadd77563","repo":"redis/redis-py","slug":"connection-closed-by-server-e52ab6","errorCode":null,"errorMessage":"Connection closed by server.","messagePattern":"Connection closed by server\\.","errorType":"exception","errorClass":"ConnectionError","httpStatus":null,"severity":"error","filePath":"redis/_parsers/resp3.py","lineNumber":67,"sourceCode":"        else:\n            if self._buffer is not None:\n                try:\n                    self._buffer.purge()\n                except AttributeError:\n                    # Buffer may have been set to None by another thread after\n                    # the check above; result is still valid so we don't raise\n                    pass\n            return result\n\n    def _read_response(\n        self,\n        disable_decoding=False,\n        push_request=False,\n        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 in (b\"-\", b\"!\"):\n            if byte == b\"!\":\n                response = self._buffer.read(int(response), timeout=timeout)\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","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/_parsers/resp3.py#L49-L85","documentation":"Raised by the sync RESP3 parser _read_response when SocketBuffer.readline() returns empty: the server closed the socket (recv returned b''). RESP3 (protocol=3, the current default on the wire) path's 'Connection closed by server.' and the RESP3 equivalent of errors 28/36.","triggerScenarios":"Any sync command over a protocol=3 connection whose reply is interrupted by a server-side close: restart, failover, CLIENT KILL, idle timeout, maxmemory-clients eviction, network drop.","commonSituations":"Default RESP3 client on a long-lived connection behind an aggressive idle reaper; failover of a managed Redis; RDB save under memory pressure dropping clients; a misconfigured 'timeout' in redis.conf.","solutions":["Configure retry_on_error=[ConnectionError, TimeoutError] + Retry/backoff.","Enable health_check_interval and socket_keepalive.","Set socket_timeout larger than any blocking-command block time.","Catch redis.exceptions.ConnectionError and reconnect/retry."],"exampleFix":"# before\nr = redis.Redis()  # protocol=3 is the default; no retry\nr.get('k')  # after a 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    health_check_interval=30, socket_keepalive=True, socket_timeout=5,\n    retry_on_error=[ConnectionError, TimeoutError],\n    retry=Retry(ExponentialWithJitterBackoff(), 3),\n)\nr.get('k')","handlingStrategy":"retry","validationCode":"from 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 resp3_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","resp3","server-closed"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}