aio-libs/aiohttp · error · WSMessageTypeError

Received message {msg.type}:{msg.data!r} is not WSMsgType.BI

Error message

Received message {msg.type}:{msg.data!r} is not WSMsgType.BINARY

What it means

Raised by WebSocketResponse.receive_bytes() as a WSMessageTypeError (a TypeError subclass) when the received message is not a BINARY frame. receive_bytes() calls receive() then asserts msg.type is WSMsgType.BINARY; a TEXT frame or a control/close message fails the check. The actual type and data are included in the message.

Source

Thrown at aiohttp/web_ws.py:701

        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,
    ) -> Any: ...

    @overload
    async def receive_json(
        self: "WebSocketResponse[Literal[False]]",
        *,
        loads: Callable[[bytes], Any] = ...,
        timeout: float | None = None,

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Use the generic receive() and branch on msg.type to handle TEXT and control frames.
  2. Verify `msg.type == WSMsgType.BINARY` before interpreting data as bytes.
  3. Catch WSMessageTypeError (from aiohttp.client_exceptions) to recover gracefully.
  4. Agree on a single frame-type convention with the peer.

Example fix

# before
async for _ in ws:
    data = await ws.receive_bytes()  # raises on TEXT/CLOSE

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

Strategy: try-catch

Validate before calling

from aiohttp import WSMsgType

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

Type guard

from aiohttp import WSMsgType

def is_binary(msg) -> bool:
    return msg.type is WSMsgType.BINARY

Try / catch

from aiohttp.client_exceptions import WSMessageTypeError

try:
    data = await ws.receive_bytes()
except WSMessageTypeError:
    msg = await ws.receive()  # handle non-binary frame

Prevention

When it happens

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

Common situations: Peer sends text but the server assumes binary; a client sending JSON text when the server expected msgpack/protobuf binary; receive_bytes() called after the connection began closing so receive() returns a CLOSE sentinel.

Related errors


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