{"record":{"id":"2ecfb87b480e9b2a","repo":"locustio/locust","slug":"zmq-sent-failure","errorCode":null,"errorMessage":"ZMQ sent failure","messagePattern":"ZMQ sent failure","errorType":"exception","errorClass":"RPCSendError","httpStatus":null,"severity":"error","filePath":"locust/rpc/zmqrpc.py","lineNumber":29,"sourceCode":"from .protocol import Message\n\n\nclass BaseSocket:\n    def __init__(self, sock_type, ipv4_only):\n        context = zmq.Context()\n        self.socket = context.socket(sock_type)\n\n        self.socket.setsockopt(zmq.TCP_KEEPALIVE, 1)\n        self.socket.setsockopt(zmq.TCP_KEEPALIVE_IDLE, 30)\n        if has_dualstack_ipv6() and not ipv4_only:\n            self.socket.setsockopt(zmq.IPV6, 1)\n\n    @retry()\n    def send(self, msg):\n        try:\n            self.socket.send(msg.serialize(), zmq.NOBLOCK)\n        except zmqerr.ZMQError as e:\n            raise RPCSendError(\"ZMQ sent failure\") from e\n\n    @retry()\n    def send_to_client(self, msg):\n        try:\n            self.socket.send_multipart([msg.node_id.encode(), msg.serialize()])\n        except zmqerr.ZMQError as e:\n            raise RPCSendError(\"ZMQ sent failure\") from e\n\n    def recv(self):\n        try:\n            data = self.socket.recv()\n            msg = Message.unserialize(data)\n        except msgerr.ExtraData as e:\n            raise RPCReceiveError(\"ZMQ interrupted message\") from e\n        except zmqerr.ZMQError as e:\n            raise RPCError(\"ZMQ network broken\") from e\n        return msg\n","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/locustio/locust/blob/f391a716e12c2c712e80b5835e877b7933397453/locust/rpc/zmqrpc.py#L11-L47","documentation":"RPCSendError raised when the ZMQ send on a ROUTER/DEALER socket fails. The library wraps the underlying zmq.error.ZMQError so distributed-runner code only has to handle locust's RPC exception types. The send uses zmq.NOBLOCK, so it fails immediately instead of blocking (e.g. when the socket cannot accept the message).","triggerScenarios":"Calling BaseSocket.send() (used by Runner.send_message / send_to_client paths) when the underlying ZMQ socket raises ZMQError on a non-blocking send — typically an unbound/closed socket, EAGAIN (HWM reached, NOBLOCK), or an unreachable peer.","commonSituations":"Master/worker test where the counterpart process crashed or the socket was closed; network partition between master and workers; sending on a socket that was never connected (worker not started or wrong host/port).","solutions":["Check that the remote master/worker process is running and the host/port are correct","Retry the message send; transient EAGAIN errors usually clear once the peer drains its queue","Verify the socket has not been closed (e.g. after Environment quit) before sending","Upgrade/verify pyzmq and libzmq versions if errors persist on valid sockets"],"exampleFix":"// before\nrunner.environment.events...  # sending after runner quit raises RPCSendError\nrunner.send_message(Message('client_stopped', None, None))\n// after\nif runner.state != STATE_STOPPED:\n    runner.send_message(Message('client_stopped', None, None))","handlingStrategy":"retry","validationCode":"if runner.state == STATE_STOPPED or runner.state == STATE_STOPPING:\n    raise RuntimeError('Runner stopped; cannot send messages')","typeGuard":null,"tryCatchPattern":"try:\n    client.send(msg)\nexcept RPCSendError:\n    logger.warning('ZMQ send failed; peer may be down')\n    # retry with backoff or mark peer unreachable","preventionTips":["Check runner state before sending during shutdown","Retry transient send failures with backoff","Monitor master/worker liveness before messaging peers"],"tags":["zmq","distributed","network","send"],"backgroundTag":"zmq-send-failure","analyzedSha":"f391a716e12c2c712e80b5835e877b7933397453","analyzedAt":"2026-08-29T00:36:13.872Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}