{"id":"67d1fda577b373ec","repo":"redis/redis-py","slug":"timeout-reading-from-host-error","errorCode":null,"errorMessage":"Timeout reading from {host_error}","messagePattern":"Timeout reading from (.+?)","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"redis/asyncio/connection.py","lineNumber":1310,"sourceCode":"                else:\n                    async with timeout_context:\n                        response = await self._read_response_from_parser(\n                            disable_decoding=disable_decoding,\n                            push_request=push_request,\n                        )\n            else:\n                response = await self._read_response_from_parser(\n                    disable_decoding=disable_decoding,\n                    push_request=push_request,\n                )\n        except asyncio.TimeoutError:\n            if timeout is not None:\n                # user requested timeout, return None. Operation can be retried\n                return None\n            # it was a self.socket_timeout error.\n            if disconnect_on_error:\n                await self.disconnect(nowait=True)\n            raise TimeoutError(f\"Timeout reading from {host_error}\")\n        except OSError as e:\n            if disconnect_on_error:\n                await self.disconnect(nowait=True)\n            raise ConnectionError(f\"Error while reading from {host_error} : {e.args}\")\n        except BaseException:\n            # Also by default close in case of BaseException.  A lot of code\n            # relies on this behaviour when doing Command/Response pairs.\n            # See #1128.\n            if disconnect_on_error:\n                await self.disconnect(nowait=True)\n            raise\n\n        if self.health_check_interval:\n            next_time = asyncio.get_running_loop().time() + self.health_check_interval\n            self.next_health_check = next_time\n\n        if isinstance(response, ResponseError):\n            raise response from None","sourceCodeStart":1292,"sourceCodeEnd":1328,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/asyncio/connection.py#L1292-L1328","documentation":"Raised as a TimeoutError from read_response() when self.socket_timeout (or the fallback) elapses before a full response is parsed. The connection is forcibly disconnected (nowait=True) because a partial read desynchronizes the protocol stream; the connection cannot be safely reused. Note: if the caller passed an explicit timeout=, the method returns None instead (so the operation can be retried) - this exception only fires for the socket_timeout path.","triggerScenarios":"read_response() with no explicit timeout while socket_timeout is set, awaiting a command that never completes (blocking commands like BLPOP with no data, or a hung server). Also when a RESP3 push/out-of-band response arrives mid-read and the parser blocks.","commonSituations":"Using blocking commands (BLPOP, BRPOP, WAIT) without passing an explicit timeout; socket_timeout smaller than the blocking operation's expected wait; server stalled (DEBUG SLEEP, fork save, AOF rewrite); pubsub get_message with the default socket_timeout.","solutions":["For blocking commands, pass an explicit timeout= to read_response (or use brpop(..., timeout=N)) so a timeout returns None instead of raising.","Set socket_timeout=None or large enough to outlive the longest blocking call.","Investigate server-side stalls (persistence fork, big Lua script, DEBUG SLEEP left on).","Avoid mixing a tight socket_timeout with long-running server-side operations on the same client."],"exampleFix":"// before\nr = redis.asyncio.Redis(host=h, port=p, socket_timeout=1)\nval = await r.brpop(\"queue\")  # blocks indefinitely -> TimeoutError after 1s\n\n// after\nr = redis.asyncio.Redis(host=h, port=p)\nval = await r.brpop(\"queue\", timeout=5)  # returns None after 5s, no exception","handlingStrategy":"validation","validationCode":"def compatible_socket_timeout(blocking_call_seconds: float | None) -> float | None:\n    # socket_timeout must exceed any blocking command's wait\n    if blocking_call_seconds is None:\n        return None\n    return blocking_call_seconds * 2","typeGuard":null,"tryCatchPattern":"from redis.exceptions import TimeoutError\nval = await r.brpop(\"q\", timeout=5)  # explicit timeout returns None, no raise\nif val is None:\n    pass  # timed out cleanly","preventionTips":["Pass an explicit timeout= to blocking commands so timeouts return None.","Don't set socket_timeout tighter than your longest blocking call.","Use pubsub's math.inf blocking read where indefinite wait is intended."],"tags":["network","timeout","read","socket-timeout","blocking","async"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}