aio-libs/aiohttp · error · WSMessageTypeError
Received message : is not WSMsgType.TEXT
Error message
Received message {msg.type}:{msg.data!r} is not WSMsgType.TEXT What it means
Raised by receive_str() when the message returned by receive() has a type other than WSMsgType.TEXT. receive_str() is a convenience that assumes the next frame is text; any other frame type (BINARY, PING, PONG, CLOSE, ERROR, or the synthetic CLOSED/CLOSING messages) trips the guard. WSMessageTypeError subclasses TypeError so it stands apart from transport errors.
Solutions
- Use the generic ws.receive() loop and branch on msg.type, only decoding when type is WSMsgType.TEXT.
- If the server legitimately sends binary, switch to receive_bytes().
- Guard the teardown: stop calling receive_str() after a CLOSE or CLOSED message.
- Handle WSMessageTypeError explicitly so control frames do not crash the reader.
Example fix
# before
async for _ in range(10):
text = await ws.receive_str()
# after
while True:
msg = await ws.receive()
if msg.type is WSMsgType.TEXT:
text = msg.data
elif msg.type in (WSMsgType.CLOSED, WSMsgType.CLOSE):
break Defensive patterns
Strategy: validation
Validate before calling
async def receive_text(ws, timeout=None):
msg = await ws.receive(timeout)
if msg.type is aiohttp.WSMsgType.TEXT:
return msg.data
if msg.type in (aiohttp.WSMsgType.CLOSED, aiohttp.WSMsgType.CLOSE):
raise ConnectionResetError('websocket closed')
return None # control frame; caller decides Type guard
from aiohttp import WSMsgType
def is_text(msg) -> bool:
return msg.type is WSMsgType.TEXT Try / catch
from aiohttp import WSMessageTypeError
try:
text = await ws.receive_str()
except WSMessageTypeError as exc:
log.warning('non-text frame ignored: %s', exc)
text = None Prevention
- Drive a receive() loop and switch on msg.type rather than calling receive_str() blindly.
- Treat CLOSE/CLOSED as a signal to stop reading.
- Negotiate subprotocol type with the server up front so text/binary expectations match.
When it happens
Trigger: Server sends a BINARY frame while the client loops on receive_str(); server initiates a CLOSE and the client calls receive_str() after; a PING/PONG control frame is the next frame in the queue; an ERROR frame (e.g. decompression failure) is delivered.
Common situations: Protocol mismatch: server treats the channel as binary, client treats it as text; not draining control frames before expecting text; continuing to call receive_str() during teardown.
Related errors
- Received message : is not WSMsgType.BINARY
- 1002
- Concurrent call to receive() is not allowed
- data argument must be byte-ish (%r)
- data argument must be str (%r)
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/facd69ad8a633d2b.
Report an issue: GitHub.
Appendix: 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 d041d4d0fd)