{"record":{"id":"9ad1ed7c8be59a33","repo":"redis/redis-py","slug":"timeout-reading-from-socket","errorCode":null,"errorMessage":"Timeout reading from socket","messagePattern":"Timeout reading from socket","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"redis/_parsers/hiredis.py","lineNumber":201,"sourceCode":"        # a shared client closed via `with redis:`); on_disconnect() sets both\n        # _sock and _reader to None. Bind them locally and fail with a\n        # descriptive, retryable ConnectionError instead of an AttributeError.\n        if sock is None or reader is None:\n            raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)\n        custom_timeout = timeout is not SENTINEL\n        try:\n            if custom_timeout:\n                sock.settimeout(timeout)\n            bufflen = sock.recv_into(self._buffer)\n            if bufflen == 0:\n                raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)\n            reader.feed(self._buffer, 0, bufflen)\n            # data was read from the socket and added to the buffer.\n            # return True to indicate that data was read.\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:\n                if not raise_on_timeout:\n                    return False\n                if timeout == 0:\n                    raise TimeoutError(\"Timeout reading from socket\")\n            raise ConnectionError(f\"Error while reading from socket: {ex.args}\")\n        finally:\n            if custom_timeout:\n                sock.settimeout(self._socket_timeout)\n\n    def read_response(","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/_parsers/hiredis.py#L183-L219","documentation":"Raised by the sync hiredis parser read_from_socket when socket.recv_into() raises socket.timeout because no bytes arrived within the active socket timeout (the connection's socket_timeout, or a per-call timeout passed via read_from_socket(timeout=)). It is a redis.exceptions.TimeoutError (error_type=NETWORK), which is distinct from Python's builtin TimeoutError and does not subclass it.","triggerScenarios":"A command whose reply takes longer to arrive than the timeout: KEYS * / SMEMBERS on a huge keyspace, large SORT, EVAL on a slow script, DEBUG SLEEP, or a blocking command (BLPOP/BZPOPMIN/XREAD BLOCK) whose block time exceeds socket_timeout. Also a congested network or a server stalled under load (RDB bgsave, AOF fsync=always stall).","commonSituations":"Default/low socket_timeout (e.g. 0.1s) against a loaded Redis; running KEYS in production; BLPOP with a timeout greater than the client socket_timeout; cross-region links with latency spikes; Redis blocked on disk I/O during AOF fsync.","solutions":["Raise socket_timeout to comfortably exceed the slowest expected command/block time.","For blocking commands, keep their block argument below socket_timeout, or pass an explicit higher per-call timeout.","Avoid KEYS/SMEMBERS in production; use SCAN and cursor pagination.","Add retry with backoff for TimeoutError via retry_on_error=[TimeoutError].","Profile long commands on the server (SLOWLOG GET) and optimize or remove them."],"exampleFix":"# before\nr = redis.Redis(socket_timeout=0.5)\nr.blpop('q', timeout=10)  # -> TimeoutError: Timeout reading from socket\n\n# after - timeout covers the block window\nr = redis.Redis(socket_timeout=12)\nr.blpop('q', timeout=10)","handlingStrategy":"retry","validationCode":"# Verify socket_timeout can host your longest op before issuing it\ndef timeout_ok(r, block_seconds):\n    st = r.connection_pool.connection_kwargs.get('socket_timeout')\n    return st is None or st > block_seconds","typeGuard":"from redis.exceptions import TimeoutError as RedisTimeoutError\n\ndef is_redis_socket_timeout(e: BaseException) -> bool:\n    return isinstance(e, RedisTimeoutError) and str(e) == 'Timeout reading from socket'","tryCatchPattern":"from redis.exceptions import TimeoutError\nimport time\nfor attempt in range(3):\n    try:\n        return r.get('k')\n    except TimeoutError:\n        if attempt == 2:\n            raise\n        time.sleep(0.2 * (2 ** attempt))","preventionTips":["Set socket_timeout strictly greater than any BLOCK time passed to BLPOP/BZPOPMIN/XREAD.","Don't catch the builtin TimeoutError - redis raises redis.exceptions.TimeoutError, which does not subclass it.","Never run KEYS in production; its wall time is unbounded and will trip any finite socket_timeout."],"tags":["timeout","network","sync","hiredis","blocking-commands"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}