aio-libs/aiohttp · error · WSMessageTypeError
Received message {msg.type}:{msg.data!r} is not WSMsgType.TE
Error message
Received message {msg.type}:{msg.data!r} is not WSMsgType.TEXT What it means
Raised by ClientWebSocketResponse.receive_str() when the next message pulled off the socket is not a TEXT frame. aiohttp guards the return type strictly because receive_str promises a str, so any other frame (BINARY, PING, PONG, CLOSE, or an ERROR/CLOSED control frame) violates that contract and surfaces as WSMessageTypeError (a TypeError subclass). The error string echoes the offending msg.type and msg.data so you can see exactly what the peer sent instead.
Source
Thrown at aiohttp/client_ws.py:481
@overload
async def receive_str(
self: "ClientWebSocketResponse[Literal[False]]", *, timeout: float | None = None
) -> bytes: ...
@overload
async def receive_str(
self: "ClientWebSocketResponse[_DecodeText]", *, timeout: float | None = None
) -> str | bytes: ...
async def receive_str(self, *, timeout: float | None = None) -> str | bytes:
"""Receive TEXT message.
Returns str when decode_text=True (default), bytes when decode_text=False.
"""
msg = await self.receive(timeout)
if msg.type is not WSMsgType.TEXT:
raise WSMessageTypeError(
f"Received message {msg.type}:{msg.data!r} is not WSMsgType.TEXT"
)
return msg.data
async def receive_bytes(self, *, timeout: float | None = None) -> bytes:
msg = await self.receive(timeout)
if msg.type is not WSMsgType.BINARY:
raise WSMessageTypeError(
f"Received message {msg.type}:{msg.data!r} is not WSMsgType.BINARY"
)
return msg.data
@overload
async def receive_json(
self: "ClientWebSocketResponse[Literal[True]]",
*,
loads: JSONDecoder = ...,
timeout: float | None = None,View on GitHub (pinned to c0ef574e29)
Solutions
- Use `msg = await ws.receive()` and dispatch on `msg.type` (WSMsgType.TEXT/BINARY/CLOSED/ERROR) instead of unconditionally calling receive_str().
- If the peer should send text, check the server implementation; the bytes you received are shown in the message.
- Guard the call: only `await ws.receive_str()` inside a loop where you have already confirmed `ws.receive()` returned TEXT, or handle WSMessageTypeError explicitly.
Example fix
// before
msg = await ws.receive_str()
// after
from aiohttp import WSMsgType
msg = await ws.receive()
if msg.type == WSMsgType.TEXT:
text = msg.data
elif msg.type in (WSMsgType.CLOSED, WSMsgType.ERROR):
raise RuntimeError(f"ws closed: {msg}") Defensive patterns
Strategy: try-catch
Validate before calling
from aiohttp import WSMsgType
async def safe_receive_str(ws):
msg = await ws.receive()
if msg.type is not WSMsgType.TEXT:
raise WSMessageTypeError(f"expected TEXT, got {msg.type}")
return msg.data Type guard
from aiohttp import WSMsgType
from aiohttp.http_websocket import WSMessage
def is_text_message(msg: WSMessage) -> bool:
return msg.type is WSMsgType.TEXT Try / catch
from aiohttp import WSMessageTypeError
try:
text = await ws.receive_str()
except WSMessageTypeError as e:
# peer sent non-text (close/binary/error); decide whether to close or recover
if ws.closed:
return None
raise Prevention
- Prefer the generic ws.receive() loop and branch on msg.type rather than receive_str() when the peer may send mixed frames.
- Treat CLOSED/ERROR as terminal and stop calling receive_str() after them.
- Document the expected frame type with the server team so the contract is explicit.
When it happens
Trigger: Calling `await ws.receive_str()` when the server just sent a BINARY frame, a CLOSE frame (peer closed), or any control frame. Also when the connection is in CLOSING/CLOSED state and receive() returns a non-TEXT message.
Common situations: Talking to a WS server that sends binary payloads (protobuf, msgpack) while the client assumes text. The peer closing the connection mid-stream. Mixing receive() with receive_str() incorrectly. A buggy server sending the wrong frame opcode.
Related errors
- Received message {msg.type}:{msg.data!r} is not WSMsgType.BI
- Invalid window size
- Extension for deflate not supported{ext}
- Invalid response status
- Invalid upgrade header
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/facd69ad8a633d2b.json.
Report an issue: GitHub.