vllm-project/vllm · error · ValueError
Malformed zmq_address {zmq_address!r}: expected 'host:IP,han
Error message
Malformed zmq_address {zmq_address!r}: expected 'host:IP,handshake:PORT,notify:PORT' format What it means
Thrown by parse_moriio_zmq_address in the MoRI-IO KV-transfer connector when the zmq_address string cannot be parsed. The connector requires the exact comma-separated form 'host:IP,handshake:PORT,notify:PORT'; a missing host/handshake/notify key (KeyError) or a non-numeric port (ValueError) is re-raised as this ValueError.
Source
Thrown at vllm/distributed/kv_transfer/kv_connector/v1/moriio/moriio_common.py:411
Parses ``"host:IP,handshake:PORT,notify:PORT"`` into
(host, handshake_port, notify_port).
Each key-value pair is split on the *first* colon so that IPv6 addresses
(e.g. ``host:::1``) are handled correctly. Raises ``ValueError`` if any
of ``host``, ``handshake``, or ``notify`` keys are absent or if the port
values are non-numeric.
"""
parts: dict[str, str] = {}
for segment in zmq_address.split(","):
key, _, val = segment.partition(":")
parts[key.strip()] = val.strip()
try:
host = parts["host"]
handshake_port = int(parts["handshake"])
notify_port = int(parts["notify"])
except (KeyError, ValueError) as e:
raise ValueError(
f"Malformed zmq_address {zmq_address!r}: expected "
f"'host:IP,handshake:PORT,notify:PORT' format"
) from e
return host, handshake_port, notify_port
def get_peer_zmq_from_request_id(request_id: str, is_producer: bool) -> str | None:
"""Extract the *peer's* zmq_address from the vLLM router request_id.
The producer (prefill) needs the decode's address; the consumer (decode)
needs the prefill's address.
Returns ``None`` when the request_id does not encode peer info. The
llm-d routing sidecar (``llm-d-inference-scheduler``) does not embed
addresses in ``request_id``; instead it passes ``remote_host``,
``remote_handshake_port`` and ``remote_notify_port`` explicitly in
``kv_transfer_params``. Callers must handle the ``None`` return by
falling back to those fields. See ``add_new_req`` for the canonicalView on GitHub (pinned to c794754062)
Solutions
- Log the offending zmq_address and diff it against 'host:IP,handshake:PORT,notify:PORT' (all three keys, integer ports)
- Fix the producer side (router or prefill instance) that generates/embeds the address so it emits all three segments correctly
- If the address is supplied via kv_transfer_params, correct remote_host / remote_handshake_port / remote_notify_port there
- Add a format check where the address is produced so malformed values fail at the source instead of at transfer time
Example fix
// before zmq_address = "tcp://10.0.0.5:5555" # wrong format, raises on parse host, hs, nf = parse_moriio_zmq_address(zmq_address) // after zmq_address = "host:10.0.0.5,handshake:5555,notify:5556" host, hs, nf = parse_moriio_zmq_address(zmq_address)
Defensive patterns
Strategy: validation
Validate before calling
import re
ZMQ_ADDR_RE = re.compile(
r"^\s*host\s*:\s*[^,\s]+\s*,\s*handshake\s*:\s*\d+\s*,\s*notify\s*:\s*\d+\s*$"
)
def is_valid_moriio_zmq_address(addr: str) -> bool:
return bool(ZMQ_ADDR_RE.match(addr))
# before parsing:
assert is_valid_moriio_zmq_address(zmq_address), f"bad zmq_address {zmq_address!r}" Type guard
def is_moriio_zmq_address(v) -> bool:
if not isinstance(v, str):
return False
parts = dict(s.partition(":")[::2] for s in v.split(","))
try:
return bool(parts.get("host", "").strip()) and int(parts["handshake"]) > 0 and int(parts["notify"]) > 0
except (KeyError, ValueError):
return False Try / catch
try:
host, hs_port, nf_port = parse_moriio_zmq_address(zmq_address)
except ValueError as e:
raise ValueError(f"routing config error for request {request_id}: {e}") from e Prevention
- Generate zmq_address strings from (host, handshake_port, notify_port) variables instead of concatenating by hand
- Validate the format at the producer/router before embedding into request_id
- Add a unit test asserting the parser rejects partial and raw-tcp:// forms
When it happens
Trigger: parse_moriio_zmq_address (directly, or via get_peer_zmq_from_request_id / build paths in moriio_common.py) is called with a string that omits one of the three key:value segments, has an empty host, or has non-integer ports. Common trigger: the router embeds a differently-formatted address into the request_id and the peer side parses it.
Common situations: Custom or older router versions that embed a different request_id format; passing a raw ZMQ endpoint like 'tcp://10.0.0.1:5555' instead of the key:value pair format; hand-typed kv_transfer_params with typos or missing ports; extra segments that overwrite expected keys.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- kv_connector_module_path cannot be an empty string.
- Invalid MoRIIO backend {backend!r} in kv_connector_extra_con
- request_id {request_id!r} does not embed a peer zmq_address
- TP sizes must be positive
- HTTP request failed: {0}
AI-assisted analysis of vllm-project/vllm@c794754062 (2026-08-14).
Data as JSON: /api/errors/8d0f9a7bfa7b9e69.
Report an issue: GitHub.