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 WebSocketResponse.receive_str() as a WSMessageTypeError (a TypeError subclass) when the message returned by receive() is not of type WSMsgType.TEXT. receive_str() expects a text frame; receiving a BINARY, PING, PONG, CLOSE, CLOSING, CLOSED, or ERROR frame trips the guard. The %r formatting includes the actual frame type and payload for diagnosis.
Solutions
- Use receive() and switch on msg.type so you can handle TEXT, BINARY, and CLOSE/ERROR separately.
- Catch WSMessageTypeError (importable from aiohttp) and treat non-text frames as a protocol error or close.
- Align client and server on frame type — if the client sends binary, switch to receive_bytes()/receive_json_bytes().
Example fix
// before
msg = await ws.receive_str() # raises on binary
// after
from aiohttp import WSMessageTypeError
try:
text = await ws.receive_str()
except WSMessageTypeError:
msg = await ws.receive()
if msg.type == WSMsgType.BINARY:
text = msg.data.decode('utf-8')
else:
raise
# or switch on type up front:
msg = await ws.receive()
if msg.type == WSMsgType.TEXT: ...
elif msg.type == WSMsgType.BINARY: ... Defensive patterns
Strategy: try-catch
Validate before calling
from aiohttp import WSMessageTypeError
try:
text = await ws.receive_str()
except WSMessageTypeError:
msg = await ws.receive()
if msg.type == WSMsgType.TEXT:
text = msg.data
else:
raise Type guard
def is_text_msg(msg) -> bool:
return msg.type is WSMsgType.TEXT Try / catch
try:
text = await ws.receive_str()
except WSMessageTypeError as e:
# peer sent non-text; decide whether to close or switch to binary handling
await ws.close(code=WSCloseCode.UNSUPPORTED_DATA, message=b'expected text')
raise Prevention
- Prefer receive() with a type dispatch over receive_str() in mixed protocols.
- Document the expected frame type in your protocol and validate the peer honours it.
- Catch WSMessageTypeError specifically (it subclasses TypeError).
When it happens
Trigger: The peer sends a BINARY frame when the server calls receive_str(); the peer sends CLOSE/CLOSED mid-stream; receive_str() is used in a loop and a control frame arrives.
Common situations: Client and server disagree on text-vs-binary framing (e.g. client sends raw bytes for JSON); a disconnect causes a CLOSE frame that receive_str does not anticipate; using receive_str when the protocol actually transmits binary.
Related errors
- Received message : is not WSMsgType.BINARY
- data argument must be byte-ish (%r)
- 1002
- 1007
- Concurrent call to receive() is not allowed
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/4d1621b6fee91575.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/web_ws.py:693
@overload
async def receive_str(
self: "WebSocketResponse[Literal[False]]", *, timeout: float | None = None
) -> bytes: ...
@overload
async def receive_str(
self: "WebSocketResponse[_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: "WebSocketResponse[Literal[True]]",
*,
loads: JSONDecoder = ...,
timeout: float | None = None,View on GitHub (pinned to d041d4d0fd)