ruvnet/RuView · error · ValueError

sensing-server emitted non-dict payload: {type(obj).__name__

Error message

sensing-server emitted non-dict payload: {type(obj).__name__}

What it means

_decode() in ws.py JSON-parses each websocket frame and requires the root to be a dict — the sensing-server protocol is a stream of JSON objects keyed by "type". A frame whose JSON root is an array, string, number, or bool violates the protocol and raises ValueError. stream() catches this per-frame and logs a WARN, but recv_one() lets the ValueError propagate to the caller.

Source

Thrown at python/wifi_densepose/client/ws.py:142

    node_id: str = ""
    timestamp: float = 0.0
    persons: tuple[dict[str, Any], ...] = ()
    confidence: float = 0.0


# ─── Decoder ─────────────────────────────────────────────────────────


def _decode(raw_text: str) -> SensingMessage:
    """Decode a single WS frame into a typed message.

    Unknown ``type`` values yield a plain ``SensingMessage`` rather
    than raising — the sensing-server is on a faster release cadence
    than this client, and unknown types should not break the stream.
    """
    obj = json.loads(raw_text)
    if not isinstance(obj, dict):
        raise ValueError(f"sensing-server emitted non-dict payload: {type(obj).__name__}")
    mtype = obj.get("type", "")
    if mtype == "connection_established":
        return ConnectionEstablishedMessage(
            type=mtype,
            raw=obj,
            node_id=obj.get("node_id", ""),
            version=obj.get("version", ""),
            capabilities=tuple(obj.get("capabilities", ())),
        )
    if mtype == "edge_vitals":
        return EdgeVitalsMessage(
            type=mtype,
            raw=obj,
            node_id=obj.get("node_id", ""),
            presence=bool(obj.get("presence", False)),
            fall_detected=bool(obj.get("fall_detected", False)),
            motion=float(obj.get("motion", 0.0)),
            breathing_rate_bpm=(

View on GitHub (pinned to 4685618388)

Solutions

  1. Use stream() instead of recv_one() — it logs and drops malformed frames without ending the iteration
  2. Verify the URL targets the sensing-server's /ws/sensing endpoint and the server version matches the client
  3. Fix custom/mock servers to emit top-level JSON objects with a "type" field

Example fix

# before
msg = await client.recv_one()  # ValueError: sensing-server emitted non-dict payload

# after
async for msg in client.stream():  # malformed frames are logged and skipped
    if isinstance(msg, EdgeVitalsMessage):
        process(msg)
Defensive patterns

Strategy: try-catch

Try / catch

while True:
    try:
        msg = await client.recv_one(timeout=5.0)
    except ValueError as e:
        log.warning("dropping malformed sensing frame: %r", e)
        continue  # skip this frame, keep the connection
    except asyncio.TimeoutError:
        break

Prevention

When it happens

Trigger: Awaiting recv_one() when the server sends a frame like `[1,2,3]` or `"ok"`; pointing SensingClient at the wrong endpoint or a mock server that emits JSON arrays / bare values instead of objects.

Common situations: Version skew between client and sensing-server; a test mock returning json.dumps(list); connecting to a non-sensing route that streams JSONL arrays.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/44e5662ed6090eed. Report an issue: GitHub.