{"record":{"id":"6db4f9fbae128bc0","repo":"redis/redis-py","slug":"connection-closed-by-server-6db4f9","errorCode":null,"errorMessage":"Connection closed by server.","messagePattern":"Connection closed by server\\.","errorType":"exception","errorClass":"ConnectionError","httpStatus":null,"severity":"error","filePath":"redis/_parsers/socket.py","lineNumber":67,"sourceCode":"        timeout: Union[float, object] = SENTINEL,\n        raise_on_timeout: Optional[bool] = True,\n    ) -> bool:\n        sock = self._sock\n        socket_read_size = self.socket_read_size\n        marker = 0\n        custom_timeout = timeout is not SENTINEL\n\n        buf = self._buffer\n        current_pos = buf.tell()\n        buf.seek(0, SEEK_END)\n        if custom_timeout:\n            sock.settimeout(timeout)\n        try:\n            while True:\n                data = sock.recv(socket_read_size)\n                # an empty string indicates the server shutdown the socket\n                if isinstance(data, bytes) and len(data) == 0:\n                    raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)\n                buf.write(data)\n                data_length = len(data)\n                marker += data_length\n\n                if length is not None and length > marker:\n                    continue\n                return True\n        except socket.timeout:\n            if raise_on_timeout:\n                raise TimeoutError(\"Timeout reading from socket\")\n            return False\n        except NONBLOCKING_EXCEPTIONS as ex:\n            # if we're in nonblocking mode and the recv raises a\n            # blocking error, simply return False indicating that\n            # there's no data to be read. otherwise raise the\n            # original exception.\n            allowed = NONBLOCKING_EXCEPTION_ERROR_NUMBERS.get(ex.__class__, -1)\n            if ex.errno == allowed:","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/redis/redis-py/blob/43bf5ac31a5d7538fad3b1928e717ba757048041/redis/_parsers/socket.py#L49-L85","documentation":"Raised by the shared SocketBuffer._read_from_socket (used by both sync pure-Python RESP2/RESP3 parsers) when sock.recv() returns b'' — the TCP EOF meaning the server closed the connection. It is the canonical server-close signal for the sync pure-Python path.","triggerScenarios":"Any sync pure-Python parser read when the server half-closes the socket: restart, `timeout`, CLIENT KILL, failover, proxy idle drop.","commonSituations":"Managed Redis/proxies reaping idle flows; rolling deploys; failover; maxmemory-clients eviction.","solutions":["Enable health_check_interval and retry_on_error=[ConnectionError, TimeoutError].","Set socket_keepalive=True.","Tune Redis `timeout`.","Catch ConnectionError and reconnect."],"exampleFix":"# before\nr = redis.Redis(host='redis')\nr.get('k')\n# after\nr = redis.Redis(host='redis', health_check_interval=30,\n                socket_keepalive=True,\n                retry_on_error=[redis.ConnectionError, redis.TimeoutError])","handlingStrategy":"retry","validationCode":"try:\n    assert r.ping()\nexcept redis.ConnectionError:\n    r.close(); r = redis.Redis(...)\nr.get('k')","typeGuard":"def is_closed_error(exc: Exception) -> bool:\n    from redis.exceptions import ConnectionError\n    return isinstance(exc, ConnectionError) and 'closed by server' in str(exc)","tryCatchPattern":"from redis.exceptions import ConnectionError\ntry:\n    return r.get('k')\nexcept ConnectionError:\n    r = redis.Redis(...)\n    return r.get('k')","preventionTips":["health_check_interval everywhere in prod.","retry_on_error for transient errors.","socket_keepalive across LB/NAT."],"tags":["network","connection","sync","socket"],"analyzedSha":"43bf5ac31a5d7538fad3b1928e717ba757048041","analyzedAt":"2026-08-06T22:35:38.048Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}