locustio/locust · error · ValueError

Invalid timeout.

Error message

Invalid timeout.

What it means

The MQTT client's selector-based network loop (_loop_selectors) raises ValueError when the given timeout is negative, since selectors cannot wait for a negative duration. This is a defensive check inside the custom gevent-compatible loop of locust's MQTT contrib client.

Source

Thrown at locust/contrib/mqtt.py:345

        reasoncode: ReasonCode,
        properties: Properties | None,
    ) -> None:
        self._on_connect_cb(client, userdata, {}, reasoncode)

    def _loop(self, timeout: float = 1.0) -> MQTTErrorCode:
        """Override the parent's _loop method to optionally use selectors.

        When use_loop_selectors is True, this uses a selector-based implementation that allows more than 340 connections.
        Otherwise, it falls back to the parent's implementation.
        """
        if self._use_loop_selectors:
            return self._loop_selectors(timeout)
        else:
            return super()._loop(timeout)

    def _loop_selectors(self, timeout: float = 1.0) -> MQTTErrorCode:
        if timeout < 0.0:
            raise ValueError("Invalid timeout.")

        sel = selectors.DefaultSelector()

        eventmask = selectors.EVENT_READ

        with suppress(IndexError):
            packet = self._out_packet.popleft()
            self._out_packet.appendleft(packet)
            eventmask = selectors.EVENT_WRITE | eventmask

        # used to check if there are any bytes left in the (SSL) socket
        pending_bytes = 0
        if hasattr(self._sock, "pending"):
            pending_bytes = self._sock.pending()  # type: ignore

        # if bytes are pending do not wait in select
        if pending_bytes > 0:
            timeout = 0.0

View on GitHub (pinned to f391a716e1)

Solutions

  1. Ensure the timeout value passed into the loop is >= 0.0
  2. Clamp the computed timeout: timeout = max(0.0, timeout)
  3. Check keepalive/timeout settings on the MQTT client configuration

Example fix

// before
client.loop(timeout=computed_deadline - now)  # may be negative
// after
timeout = max(0.0, computed_deadline - now)
client.loop(timeout=timeout)
Defensive patterns

Strategy: validation

Validate before calling

timeout = max(0.0, timeout)
assert timeout >= 0.0, f"MQTT loop timeout must be non-negative, got {timeout}"

Prevention

When it happens

Trigger: Constructing or configuring the MQTT user/client such that the loop timeout resolves below 0.0 (e.g. keepalive or timeout math yielding a negative value) while the selectors-based loop path is active.

Common situations: Custom timeout configuration in a Milvus/MQTT locustfile; clock/deadline calculations passing past deadlines as negative timeouts; subclass overriding _loop and passing a bad timeout.

Understand the failure class

Related errors


AI-assisted analysis of locustio/locust@f391a716e1 (2026-08-29). Data as JSON: /api/errors/f86214725a354ce7. Report an issue: GitHub.