{"record":{"id":"39000fb6bec459e0","repo":"redis/redis-py","slug":"error-errno-while-writing-to-socket-errmsg","errorCode":null,"errorMessage":"Error {errno} while writing to socket. {errmsg}.","messagePattern":"Error (.+?) while writing to socket\\. (.+?)\\.","errorType":"exception","errorClass":"ConnectionError","httpStatus":null,"severity":"error","filePath":"redis/connection.py","lineNumber":1360,"sourceCode":"        # guard against health check recursion\n        if check_health:\n            self.check_health()\n        try:\n            if isinstance(command, str):\n                command = [command]\n            for item in command:\n                self._sock.sendall(item)\n        except socket.timeout:\n            self.disconnect()\n            raise TimeoutError(\"Timeout writing to socket\")\n        except OSError as e:\n            self.disconnect()\n            if len(e.args) == 1:\n                errno, errmsg = \"UNKNOWN\", e.args[0]\n            else:\n                errno = e.args[0]\n                errmsg = e.args[1]\n            raise ConnectionError(f\"Error {errno} while writing to socket. {errmsg}.\")\n        except BaseException:\n            # BaseExceptions can be raised when a socket send operation is not\n            # finished, e.g. due to a timeout.  Ideally, a caller could then re-try\n            # to send un-sent data. However, the send_packed_command() API\n            # does not support it so there is no point in keeping the connection open.\n            self.disconnect()\n            raise\n\n    def send_command(self, *args, **kwargs):\n        \"\"\"Pack and send a command to the Redis server\"\"\"\n        self.send_packed_command(\n            self._command_packer.pack(*args),\n            check_health=kwargs.get(\"check_health\", True),\n        )\n\n    def can_read(self, timeout: float = 0) -> bool:\n        \"\"\"Poll the socket to see if there's data that can be read.\"\"\"\n        # TODO: Rename this API; it detects pending data or dirty/closed","sourceCodeStart":1342,"sourceCodeEnd":1378,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/connection.py#L1342-L1378","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Catch ConnectionError and reconnect/retry the operation (the pool handles this for pooled clients).","Ensure redis.ConnectionPool/Sentinel are used so dead connections are reaped and replaced.","Lower socket TCP keepalive idle / LB idle timeout or enable socket_keepalive to detect dead peers sooner.","Check INFO clients / maxclients on the server.","Avoid sharing a single connection across threads; use the client/pool."],"exampleFix":"# before\nconn = redis.Redis(host=h, port=p).connection_manager  # manual reuse\n# after — let the pool recover\nr = redis.Redis(host=h, port=p, socket_keepalive=True)\ntry:\n    r.set('k','v')\nexcept redis.ConnectionError:\n    r.set('k','v')  # pool opens a fresh connection","handlingStrategy":"try-catch","validationCode":"# Prefer pooled clients that recover automatically; tune keepalive\nr = redis.Redis(\n    host=h, port=p,\n    socket_keepalive=True,\n    health_check_interval=30,\n    retry_on_error=[redis.ConnectionError],\n    retry=Retry(ExponentialBackoff(), 3),\n)","typeGuard":"null","tryCatchPattern":"from redis.exceptions import ConnectionError\nfor _ in range(3):\n    try:\n        r.set('k', 'v')\n        break\n    except ConnectionError:\n        continue  # pool opens a fresh connection; safe for idempotent ops","preventionTips":["Use the connection pool / Sentinel so dropped connections are replaced.","Enable socket_keepalive to detect broken peers faster.","Watch INFO clients/maxclients and dmesg for OOM/resets.","Don't share a single connection across threads."],"tags":["network","write","connection-reset","oserror"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}