{"record":{"id":"44e5662ed6090eed","repo":"ruvnet/RuView","slug":"sensing-server-emitted-non-dict-payload-type-obj","errorCode":null,"errorMessage":"sensing-server emitted non-dict payload: {type(obj).__name__}","messagePattern":"sensing-server emitted non-dict payload: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/wifi_densepose/client/ws.py","lineNumber":142,"sourceCode":"    node_id: str = \"\"\n    timestamp: float = 0.0\n    persons: tuple[dict[str, Any], ...] = ()\n    confidence: float = 0.0\n\n\n# ─── Decoder ─────────────────────────────────────────────────────────\n\n\ndef _decode(raw_text: str) -> SensingMessage:\n    \"\"\"Decode a single WS frame into a typed message.\n\n    Unknown ``type`` values yield a plain ``SensingMessage`` rather\n    than raising — the sensing-server is on a faster release cadence\n    than this client, and unknown types should not break the stream.\n    \"\"\"\n    obj = json.loads(raw_text)\n    if not isinstance(obj, dict):\n        raise ValueError(f\"sensing-server emitted non-dict payload: {type(obj).__name__}\")\n    mtype = obj.get(\"type\", \"\")\n    if mtype == \"connection_established\":\n        return ConnectionEstablishedMessage(\n            type=mtype,\n            raw=obj,\n            node_id=obj.get(\"node_id\", \"\"),\n            version=obj.get(\"version\", \"\"),\n            capabilities=tuple(obj.get(\"capabilities\", ())),\n        )\n    if mtype == \"edge_vitals\":\n        return EdgeVitalsMessage(\n            type=mtype,\n            raw=obj,\n            node_id=obj.get(\"node_id\", \"\"),\n            presence=bool(obj.get(\"presence\", False)),\n            fall_detected=bool(obj.get(\"fall_detected\", False)),\n            motion=float(obj.get(\"motion\", 0.0)),\n            breathing_rate_bpm=(","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/python/wifi_densepose/client/ws.py#L124-L160","documentation":"_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.","triggerScenarios":"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.","commonSituations":"Version skew between client and sensing-server; a test mock returning json.dumps(list); connecting to a non-sensing route that streams JSONL arrays.","solutions":["Use stream() instead of recv_one() — it logs and drops malformed frames without ending the iteration","Verify the URL targets the sensing-server's /ws/sensing endpoint and the server version matches the client","Fix custom/mock servers to emit top-level JSON objects with a \"type\" field"],"exampleFix":"# before\nmsg = await client.recv_one()  # ValueError: sensing-server emitted non-dict payload\n\n# after\nasync for msg in client.stream():  # malformed frames are logged and skipped\n    if isinstance(msg, EdgeVitalsMessage):\n        process(msg)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"while True:\n    try:\n        msg = await client.recv_one(timeout=5.0)\n    except ValueError as e:\n        log.warning(\"dropping malformed sensing frame: %r\", e)\n        continue  # skip this frame, keep the connection\n    except asyncio.TimeoutError:\n        break","preventionTips":["Prefer stream() for continuous consumption — it already isolates per-frame decode errors","Pin client and sensing-server versions together in deployment","Contract-test custom/mock servers to emit top-level JSON objects with a type field"],"tags":["websocket","json","protocol","validation","decode"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}