redis/redis-py · error · TimeoutError

Timeout reading from socket

Error message

Timeout reading from socket

What it means

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.

Solutions

  1. Raise socket_timeout to comfortably exceed the slowest expected command/block time.
  2. For blocking commands, keep their block argument below socket_timeout, or pass an explicit higher per-call timeout.
  3. Avoid KEYS/SMEMBERS in production; use SCAN and cursor pagination.
  4. Add retry with backoff for TimeoutError via retry_on_error=[TimeoutError].
  5. Profile long commands on the server (SLOWLOG GET) and optimize or remove them.

Example fix

# before
r = redis.Redis(socket_timeout=0.5)
r.blpop('q', timeout=10)  # -> TimeoutError: Timeout reading from socket

# after - timeout covers the block window
r = redis.Redis(socket_timeout=12)
r.blpop('q', timeout=10)
Defensive patterns

Strategy: retry

Validate before calling

# Verify socket_timeout can host your longest op before issuing it
def timeout_ok(r, block_seconds):
    st = r.connection_pool.connection_kwargs.get('socket_timeout')
    return st is None or st > block_seconds

Type guard

from redis.exceptions import TimeoutError as RedisTimeoutError

def is_redis_socket_timeout(e: BaseException) -> bool:
    return isinstance(e, RedisTimeoutError) and str(e) == 'Timeout reading from socket'

Try / catch

from redis.exceptions import TimeoutError
import time
for attempt in range(3):
    try:
        return r.get('k')
    except TimeoutError:
        if attempt == 2:
            raise
        time.sleep(0.2 * (2 ** attempt))

Prevention

When it happens

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

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

Understand the failure class

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/9ad1ed7c8be59a33. Report an issue: GitHub.

Appendix: source

Thrown at redis/_parsers/hiredis.py:201

        # a shared client closed via `with redis:`); on_disconnect() sets both
        # _sock and _reader to None. Bind them locally and fail with a
        # descriptive, retryable ConnectionError instead of an AttributeError.
        if sock is None or reader is None:
            raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)
        custom_timeout = timeout is not SENTINEL
        try:
            if custom_timeout:
                sock.settimeout(timeout)
            bufflen = sock.recv_into(self._buffer)
            if bufflen == 0:
                raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)
            reader.feed(self._buffer, 0, bufflen)
            # data was read from the socket and added to the buffer.
            # return True to indicate that data was read.
            return True
        except socket.timeout:
            if raise_on_timeout:
                raise TimeoutError("Timeout reading from socket")
            return False
        except NONBLOCKING_EXCEPTIONS as ex:
            # if we're in nonblocking mode and the recv raises a
            # blocking error, simply return False indicating that
            # there's no data to be read. otherwise raise the
            # original exception.
            allowed = NONBLOCKING_EXCEPTION_ERROR_NUMBERS.get(ex.__class__, -1)
            if ex.errno == allowed:
                if not raise_on_timeout:
                    return False
                if timeout == 0:
                    raise TimeoutError("Timeout reading from socket")
            raise ConnectionError(f"Error while reading from socket: {ex.args}")
        finally:
            if custom_timeout:
                sock.settimeout(self._socket_timeout)

    def read_response(

View on GitHub (pinned to 6a6b581b48)