redis/redis-py · error · ConnectionError

Error while writing to socket. .

Error message

Error {errno} while writing to socket. {errmsg}.

What it means

Raised in send_packed_command (connection.py:1353-1360) as the generic OSError branch during socket.sendall, after the socket.timeout branch. The actual errno and OS message are formatted into the string (e.g. EPIPE, ECONNRESET, EBADF). The connection is disconnected first, then ConnectionError is raised wrapping the OS-level cause.

Solutions

  1. Catch ConnectionError and reconnect/retry the operation (the pool handles this for pooled clients).
  2. Ensure redis.ConnectionPool/Sentinel are used so dead connections are reaped and replaced.
  3. Lower socket TCP keepalive idle / LB idle timeout or enable socket_keepalive to detect dead peers sooner.
  4. Check INFO clients / maxclients on the server.
  5. Avoid sharing a single connection across threads; use the client/pool.

Example fix

# before
conn = redis.Redis(host=h, port=p).connection_manager  # manual reuse
# after — let the pool recover
r = redis.Redis(host=h, port=p, socket_keepalive=True)
try:
    r.set('k','v')
except redis.ConnectionError:
    r.set('k','v')  # pool opens a fresh connection
Defensive patterns

Strategy: try-catch

Validate before calling

# Prefer pooled clients that recover automatically; tune keepalive
r = redis.Redis(
    host=h, port=p,
    socket_keepalive=True,
    health_check_interval=30,
    retry_on_error=[redis.ConnectionError],
    retry=Retry(ExponentialBackoff(), 3),
)

Type guard

null

Try / catch

from redis.exceptions import ConnectionError
for _ in range(3):
    try:
        r.set('k', 'v')
        break
    except ConnectionError:
        continue  # pool opens a fresh connection; safe for idempotent ops

Prevention

When it happens

Trigger: The server reset the connection (ECONNRESET), the local socket was already closed (EBADF), a broken pipe because the peer closed (EPIPE), or any other non-timeout OSError while sending a command.

Common situations: Redis restarted/failed over under a long-lived connection; maxclients reached and the server drops you; firewall kill; LB idle timeout closing the socket; connection used after disconnect in another thread.

Related errors


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

Appendix: source

Thrown at redis/connection.py:1360

        # 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"""
        self.send_packed_command(
            self._command_packer.pack(*args),
            check_health=kwargs.get("check_health", True),
        )

    def can_read(self, timeout: float = 0) -> bool:
        """Poll the socket to see if there's data that can be read."""
        # TODO: Rename this API; it detects pending data or dirty/closed

View on GitHub (pinned to 6a6b581b48)