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 WebSocketResponse.receive_str() as a WSMessageTypeError (a TypeError subclass) when the received message is not a TEXT frame. receive_str() calls receive() then asserts msg.type is WSMsgType.TEXT; a BINARY frame, or a control/close message returned by receive(), fails the check. The message includes the actual type and data for diagnostics.

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 c0ef574e29)

Solutions

  1. Use the generic receive() and branch on msg.type so you can handle BINARY and control frames.
  2. Check `msg.type` before treating data as text; only call receive_str() when you know the peer sends TEXT.
  3. Catch WSMessageTypeError (from aiohttp.client_exceptions) to recover when a non-text frame arrives.
  4. Coordinate the wire protocol with the peer so both sides agree on text vs binary.

Example fix

# before
async for _ in ws:
    text = await ws.receive_str()  # raises on BINARY/CLOSE

# after
async for msg in ws:
    if msg.type == WSMsgType.TEXT:
        handle_text(msg.data)
    elif msg.type == WSMsgType.BINARY:
        handle_binary(msg.data)
Defensive patterns

Strategy: try-catch

Validate before calling

from aiohttp import WSMsgType
from aiohttp.client_exceptions import WSMessageTypeError

async def receive_text(ws):
    msg = await ws.receive()
    if msg.type is WSMsgType.TEXT:
        return msg.data
    return None

Type guard

from aiohttp import WSMsgType

def is_text(msg) -> bool:
    return msg.type is WSMsgType.TEXT

Try / catch

from aiohttp.client_exceptions import WSMessageTypeError

try:
    text = await ws.receive_str()
except WSMessageTypeError:
    # peer sent a non-text frame; handle via generic receive()
    msg = await ws.receive()

Prevention

When it happens

Trigger: Calling `await ws.receive_str()` when the peer sent a BINARY frame, or when receive() returned a CLOSE/CLOSING/CLOSED/PING/PONG control message. The check at aiohttp/web_ws.py:692-695 raises.

Common situations: Peer sends binary data but the server assumes text; mixing JSON-as-text and binary attachments on the same socket; receive_str() called after the connection started closing, so receive() returns a CLOSE sentinel; a client that switches frame types mid-stream.

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/4d1621b6fee91575.json. Report an issue: GitHub.