redis/redis-py · error · TimeoutError

Timeout reading from {host_error}

Error message

Timeout reading from {host_error}

What it means

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.

Source

Thrown at redis/asyncio/connection.py:1310

                else:
                    async with timeout_context:
                        response = await self._read_response_from_parser(
                            disable_decoding=disable_decoding,
                            push_request=push_request,
                        )
            else:
                response = await self._read_response_from_parser(
                    disable_decoding=disable_decoding,
                    push_request=push_request,
                )
        except asyncio.TimeoutError:
            if timeout is not None:
                # user requested timeout, return None. Operation can be retried
                return None
            # it was a self.socket_timeout error.
            if disconnect_on_error:
                await self.disconnect(nowait=True)
            raise TimeoutError(f"Timeout reading from {host_error}")
        except OSError as e:
            if disconnect_on_error:
                await self.disconnect(nowait=True)
            raise ConnectionError(f"Error while reading from {host_error} : {e.args}")
        except BaseException:
            # Also by default close in case of BaseException.  A lot of code
            # relies on this behaviour when doing Command/Response pairs.
            # See #1128.
            if disconnect_on_error:
                await self.disconnect(nowait=True)
            raise

        if self.health_check_interval:
            next_time = asyncio.get_running_loop().time() + self.health_check_interval
            self.next_health_check = next_time

        if isinstance(response, ResponseError):
            raise response from None

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. For blocking commands, pass an explicit timeout= to read_response (or use brpop(..., timeout=N)) so a timeout returns None instead of raising.
  2. Set socket_timeout=None or large enough to outlive the longest blocking call.
  3. Investigate server-side stalls (persistence fork, big Lua script, DEBUG SLEEP left on).
  4. Avoid mixing a tight socket_timeout with long-running server-side operations on the same client.

Example fix

// before
r = redis.asyncio.Redis(host=h, port=p, socket_timeout=1)
val = await r.brpop("queue")  # blocks indefinitely -> TimeoutError after 1s

// after
r = redis.asyncio.Redis(host=h, port=p)
val = await r.brpop("queue", timeout=5)  # returns None after 5s, no exception
Defensive patterns

Strategy: validation

Validate before calling

def compatible_socket_timeout(blocking_call_seconds: float | None) -> float | None:
    # socket_timeout must exceed any blocking command's wait
    if blocking_call_seconds is None:
        return None
    return blocking_call_seconds * 2

Try / catch

from redis.exceptions import TimeoutError
val = await r.brpop("q", timeout=5)  # explicit timeout returns None, no raise
if val is None:
    pass  # timed out cleanly

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/67d1fda577b373ec.json. Report an issue: GitHub.