redis/redis-py · error · TimeoutError

Timeout writing to socket

Error message

Timeout writing to socket

What it means

Raised in send_packed_command (connection.py:1350-1352) when socket.sendall raises socket.timeout while writing a command to the server. The socket's write timeout (socket_timeout) elapsed before all bytes were sent, so the connection is disconnected and a redis.exceptions.TimeoutError is raised. This is a write-side stall, distinct from a read timeout.

Solutions

  1. Increase socket_timeout to comfortably exceed your largest expected send time.
  2. Batch smaller pipelines or split very large commands.
  3. Check for server-side stalls (slowlog, LATENCY, INFO persistence — bgsave in progress).
  4. Add retry_on_timeout=True / a Retry policy to recover from transient stalls.
  5. Scale the Redis instance or reduce concurrent load.

Example fix

# before
r = redis.Redis(host=h, port=p, socket_timeout=0.1)
# after
r = redis.Redis(host=h, port=p, socket_timeout=2.0, retry_on_timeout=True)
Defensive patterns

Strategy: retry

Validate before calling

# Size timeouts for your largest payload; enable retry
r = redis.Redis(
    host=h, port=p,
    socket_timeout=max(2.0, estimated_worst_send_seconds),
    retry_on_timeout=True,
    retry=Retry(ExponentialBackoff(), 3),
)

Type guard

null

Try / catch

from redis.exceptions import TimeoutError
for _ in range(3):
    try:
        r.execute_command(*cmd)
        break
    except TimeoutError as e:
        if 'writing to socket' in str(e):
            continue  # transient; pool reopens
        raise

Prevention

When it happens

Trigger: A command is sent on a connection whose socket_timeout is set, and the server/network is too slow to accept the bytes within that window (server paused, GC stop-the-world, saturated link, paused VM).

Common situations: Large PIPELINE/MULTI payloads on a slow link; Redis blocked (DEBUG SLEEP, slow script/LUA, big SAVE); cloud network blips; socket_timeout set too aggressively; overloaded instance under memory pressure / fork (BGSAVE).

Understand the failure class

Related errors


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

Appendix: source

Thrown at redis/connection.py:1352

                self._ping_failed,
                with_failure_count=True,
            )

    def send_packed_command(self, command, check_health=True):
        """Send an already packed command to the Redis server"""
        if not self._sock:
            self.connect_check_health(check_health=False)
        # guard against health check recursion
        if check_health:
            self.check_health()
        try:
            if isinstance(command, str):
                command = [command]
            for item in command:
                self._sock.sendall(item)
        except socket.timeout:
            self.disconnect()
            raise TimeoutError("Timeout writing to socket")
        except OSError as e:
            self.disconnect()
            if len(e.args) == 1:
                errno, errmsg = "UNKNOWN", e.args[0]
            else:
                errno = e.args[0]
                errmsg = e.args[1]
            raise ConnectionError(f"Error {errno} while writing to socket. {errmsg}.")
        except BaseException:
            # BaseExceptions can be raised when a socket send operation is not
            # finished, e.g. due to a timeout.  Ideally, a caller could then re-try
            # to send un-sent data. However, the send_packed_command() API
            # does not support it so there is no point in keeping the connection open.
            self.disconnect()
            raise

    def send_command(self, *args, **kwargs):
        """Pack and send a command to the Redis server"""

View on GitHub (pinned to 6a6b581b48)