{"record":{"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/6a6b581b48225afa0b76912d1028c6035baee932/redis/asyncio/connection.py#L1292-L1328","documentation":"Raised as TimeoutError from read_response() when the read_timeout (self.socket_timeout, or an explicit per-call timeout that is None) elapses before the parser produces a response. Only raised when the user did NOT pass an explicit timeout; passing an explicit timeout that elapses returns None instead (the operation is retried). The connection is disconnected nowait before raising.","triggerScenarios":"Any await of a response (command read, pubsub poll, BLPOP-style blocking command) when socket_timeout is set and the server has not replied in time. Distinct from a blocking-command wait: this is the socket-level budget, and it kills the connection.","commonSituations":"socket_timeout smaller than a legitimate BLPOP/BRPOP/XREAD block duration; server stalled (slowlog, fork during save, AOF fsync); large response stuck behind head-of-line blocking; cross-region latency spikes.","solutions":["Raise socket_timeout to exceed the longest legitimate blocking command.","For blocking commands, pass an explicit timeout= to read_response()/parse_response() so you get None instead of an exception.","Investigate server slowlog, AOF/fsync latency, and fork latency (INFO stats, LATENCY events).","Use command_timeout or per-command timeouts instead of a single global socket_timeout when workloads vary."],"exampleFix":"// before\nr = redis.asyncio.Redis(host=h, socket_timeout=1.0)\nv = await r.blpop('q', timeout=10)  # always times out the socket first\n// after\nr = redis.asyncio.Redis(host=h)  # no socket_timeout\nv = await r.blpop('q', timeout=10)","handlingStrategy":"try-catch","validationCode":"def socket_timeout_for_block(block_seconds: float) -> float | None:\n    # don't set a socket_timeout smaller than the longest blocking wait\n    return None if block_seconds > 0 else 5.0","typeGuard":"def is_read_timeout(exc: BaseException) -> bool:\n    return isinstance(exc, TimeoutError) and 'reading from' in str(exc).lower()","tryCatchPattern":"from redis.retry import Retry\nfrom redis.backoff import ExponentialBackoff\n\nr = redis.asyncio.Redis(\n    host=h,\n    socket_timeout=5.0,\n    retry=Retry(ExponentialBackoff(), 2),\n    retry_on_timeout=True,\n)","preventionTips":["Do not set socket_timeout below your longest blocking-command wait.","Pass explicit timeout= to read_response() for blocking commands (returns None instead).","Monitor server slowlog and fork/fsync latency."],"tags":["network","timeout","read","socket-timeout","async","blocking-command","host-error"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}