{"record":{"id":"9b4249617dcb4902","repo":"ruvnet/RuView","slug":"sensingclient-not-connected-use-async-with-firs","errorCode":null,"errorMessage":"SensingClient not connected. Use `async with` first.","messagePattern":"SensingClient not connected\\. Use `async with` first\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"python/wifi_densepose/client/ws.py","lineNumber":270,"sourceCode":"\n    async def close(self) -> None:\n        \"\"\"Idempotent connection close.\"\"\"\n        if self._ws is not None:\n            try:\n                await self._ws.close()\n            except Exception as e:  # pragma: no cover — best-effort close\n                log.debug(\"ignored WS close error: %r\", e)\n            self._ws = None\n\n    async def stream(self) -> AsyncIterator[SensingMessage]:\n        \"\"\"Yield typed messages until the server closes the connection\n        or the context is exited.\n\n        Decode failures on individual frames are logged at WARN and\n        swallowed — a malformed frame should not terminate the stream\n        (the next frame may be fine).\"\"\"\n        if self._ws is None:\n            raise RuntimeError(\"SensingClient not connected. Use `async with` first.\")\n        try:\n            async for frame in self._ws:\n                if isinstance(frame, bytes):\n                    frame = frame.decode(\"utf-8\", errors=\"replace\")\n                try:\n                    yield _decode(frame)\n                except (ValueError, json.JSONDecodeError) as e:\n                    log.warning(\"dropping malformed sensing-server frame: %r\", e)\n        except ConnectionClosed:\n            # Graceful EOF — exit the iterator normally.\n            return\n\n    async def send_ping(self) -> None:\n        \"\"\"Send an application-level ping. The sensing-server replies\n        with `{\"type\": \"pong\"}` (main.rs:2698).\"\"\"\n        if self._ws is None:\n            raise RuntimeError(\"SensingClient not connected. Use `async with` first.\")\n        await self._ws.send(json.dumps({\"type\": \"ping\"}))","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/python/wifi_densepose/client/ws.py#L252-L288","documentation":"SensingClient.stream() iterates self._ws, which is only assigned in __aenter__ (via websockets.connect) and reset to None by close(). Calling stream() on a client that never entered its `async with` block — or already exited it — raises RuntimeError with the usage hint.","triggerScenarios":"`client = SensingClient(url)` followed by `async for msg in client.stream():` without `async with client:`; or calling stream() after close()/__aexit__ set _ws back to None.","commonSituations":"Refactoring away from a manual connect() API; copy-pasting from synchronous examples; a task started before the context manager was entered.","solutions":["Wrap usage in the async context manager: `async with SensingClient(url) as client: ...`","Only iterate stream() while the context is active; treat close() as terminal","For reconnection, wrap the whole `async with` in an application-level retry loop (the client intentionally does not auto-reconnect)"],"exampleFix":"# before\nclient = SensingClient(\"ws://localhost:8765/ws/sensing\")\nasync for msg in client.stream():  # RuntimeError: not connected\n    ...\n\n# after\nasync with SensingClient(\"ws://localhost:8765/ws/sensing\") as client:\n    async for msg in client.stream():\n        ...","handlingStrategy":"validation","validationCode":"def sensing_connected(client) -> bool:\n    \"\"\"True while inside an `async with` block (connection is open).\"\"\"\n    return client._ws is not None\n\n# only stream while the context is active:\nasync with SensingClient(url) as client:\n    assert sensing_connected(client)\n    async for msg in client.stream():\n        ...","typeGuard":null,"tryCatchPattern":"try:\n    async for msg in client.stream():\n        ...\nexcept RuntimeError as e:\n    if \"not connected\" in str(e):\n        raise RuntimeError(\n            \"stream() must run inside `async with SensingClient(...)`\"\n        ) from e\n    raise","preventionTips":["Construct and use SensingClient within one function scope so the context manager cannot be skipped","Never hold a reference past the `async with` block; close() is terminal","Wrap the whole `async with` in a retry loop for reconnection instead of reusing a closed client"],"tags":["python","async","websocket","lifecycle","state-machine","context-manager"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}