{"id":"9cf553a76ce07e40","repo":"aio-libs/aiohttp","slug":"cannot-call-write-for-websocket","errorCode":null,"errorMessage":"Cannot call .write() for websocket","messagePattern":"Cannot call \\.write\\(\\) for websocket","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_ws.py","lineNumber":742,"sourceCode":"        self: \"WebSocketResponse[_DecodeText]\",\n        *,\n        loads: JSONDecoder | Callable[[bytes], Any] = ...,\n        timeout: float | None = None,\n    ) -> Any: ...\n\n    async def receive_json(\n        self,\n        *,\n        loads: JSONDecoder | Callable[[bytes], Any] = json.loads,\n        timeout: float | None = None,\n    ) -> Any:\n        data = await self.receive_str(timeout=timeout)\n        return loads(data)  # type: ignore[arg-type]\n\n    async def write(\n        self, data: Union[bytes, bytearray, \"memoryview[int]\", \"memoryview[bytes]\"]\n    ) -> None:\n        raise RuntimeError(\"Cannot call .write() for websocket\")\n\n    def __aiter__(self) -> Self:\n        return self\n\n    @overload\n    async def __anext__(\n        self: \"WebSocketResponse[Literal[True]]\",\n    ) -> WSMessageDecodeText: ...\n\n    @overload\n    async def __anext__(\n        self: \"WebSocketResponse[Literal[False]]\",\n    ) -> WSMessageNoDecodeText: ...\n\n    @overload\n    async def __anext__(\n        self: \"WebSocketResponse[_DecodeText]\",\n    ) -> WSMessageDecodeText | WSMessageNoDecodeText: ...","sourceCodeStart":724,"sourceCodeEnd":760,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/web_ws.py#L724-L760","documentation":"Raised unconditionally by WebSocketResponse.write() (aiohttp/web_ws.py:739-742). WebSocketResponse inherits write() from StreamResponse but overrides it to always raise, because raw byte writes bypass WebSocket framing. You must use the frame-aware send_str/send_bytes/send_json methods which construct proper WebSocket frames via the internal writer.","triggerScenarios":"Calling `await ws.write(data)` on a WebSocketResponse at any time, prepared or not. The method body is just `raise RuntimeError(...)`.","commonSituations":"Treating a WebSocketResponse like a normal StreamResponse and calling write(); code copied from a streaming HTTP handler into a WS handler; a framework abstraction that calls write() generically on response objects.","solutions":["Replace `await ws.write(data)` with `await ws.send_bytes(data)` for binary or `await ws.send_str(data)` for text.","Use send_json()/send_json_bytes() for JSON payloads.","Remove any generic write() call path that targets a WebSocketResponse."],"exampleFix":"# before\nawait ws.write(b'chunk')\n\n# after\nawait ws.send_bytes(b'chunk')","handlingStrategy":"validation","validationCode":"async def ws_send(ws, data):\n    if isinstance(data, (bytes, bytearray, memoryview)):\n        await ws.send_bytes(data)\n    else:\n        await ws.send_str(data)","typeGuard":"def is_websocket_response(resp) -> bool:\n    return type(resp).__name__ == \"WebSocketResponse\"","tryCatchPattern":"try:\n    await ws.write(data)\nexcept RuntimeError as e:\n    if \"Cannot call .write() for websocket\" in str(e):\n        await ws.send_bytes(data if isinstance(data, (bytes, bytearray, memoryview)) else str(data).encode())\n    else:\n        raise","preventionTips":["Never call write() on a WebSocketResponse; use send_str/send_bytes/send_json.","In generic response code, special-case WebSocketResponse before write().","Static type checkers/mypy will flag write() on the overridden method."],"tags":["websocket","server","api-misuse","write"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}