redis/redis-py · error · ConnectionError

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

Error message

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

What it means

Raised as a ConnectionError from send_packed_command() when the underlying write raises an OSError (other than a timeout). The errno and message are extracted from e.args; the connection is disconnected (nowait=True). This is the generic 'broken socket while writing' failure covering EPIPE, ECONNRESET, EBADF, etc.

Source

Thrown at redis/asyncio/connection.py:1201

                command = [command]
            if self.socket_timeout:
                await asyncio.wait_for(
                    self._send_packed_command(command), self.socket_timeout
                )
            else:
                self._writer.writelines(command)
                await self._writer.drain()
        except asyncio.TimeoutError:
            await self.disconnect(nowait=True)
            raise TimeoutError("Timeout writing to socket") from None
        except OSError as e:
            await self.disconnect(nowait=True)
            if len(e.args) == 1:
                err_no, errmsg = "UNKNOWN", e.args[0]
            else:
                err_no = e.args[0]
                errmsg = e.args[1]
            raise ConnectionError(
                f"Error {err_no} while writing to socket. {errmsg}."
            ) from e
        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.
            await self.disconnect(nowait=True)
            raise

    async def send_command(self, *args: Any, **kwargs: Any) -> None:
        """Pack and send a command to the Redis server"""
        await self.send_packed_command(
            self.pack_command(*args), check_health=kwargs.get("check_health", True)
        )

    @deprecated_function(
        version="8.0.0", reason="Use can_read() instead", name="can_read_destructive"

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Wrap command execution in retry logic (the client supports retry_on_error / a configured Retry with backoff).
  2. Ensure firewalls/load balancers send keepalives or raise their idle timeout; enable socket_keepalive on the client.
  3. Set a health_check_interval so dead connections are detected before command use.
  4. Inspect the reported errno (ECONNRESET vs EBADF vs ENETUNREACH) to pinpoint server vs network vs local-state.

Example fix

// before
r = redis.asyncio.Redis(host=h, port=p)

// after
from redis.backoff import ExponentialWithJitterBackoff
from redis.retry import Retry
retry = Retry(ExponentialWithJitterBackoff(), 3)
r = redis.asyncio.Redis(host=h, port=p, retry=retry, retry_on_error=[ConnectionError])
Defensive patterns

Strategy: retry

Try / catch

from redis.exceptions import ConnectionError
for attempt in range(3):
    try:
        await r.set("k", "v")
        break
    except ConnectionError as e:
        if "while writing to socket" in str(e):
            await asyncio.sleep(2 ** attempt)
            continue
        raise

Prevention

When it happens

Trigger: Any send_command/send_packed_command where writer.writelines/drain raises OSError: writing to a connection the server already closed (EPIPE/ECONNRESET), a connection whose socket was closed locally (EBADF), or an OS-level IO error.

Common situations: Server restarted/crashed mid-session; a firewall/ELB idle timeout silently dropped the connection and the kernel only reports it on the next write; client-side disconnect racing with a command; running against an ACL-disabled or memory-evicted connection; ephemeral-port exhaustion.

Related errors


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