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 ClientWebSocketResponse.receive_bytes() when the next message is not a BINARY frame. receive_bytes promises bytes, so a TEXT frame, a CLOSE/CLOSED control frame, or an ERROR frame makes the contract impossible to keep and aiohttp raises WSMessageTypeError (TypeError subclass). The offending type and data are included in the message for debugging.

Source

Thrown at aiohttp/client_ws.py:489

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

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

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Switch to `msg = await ws.receive()` and branch on `msg.type == WSMsgType.BINARY`.
  2. Verify the server actually sends BINARY frames; the message shows the actual frame type received.
  3. Catch WSMessageTypeError around receive_bytes() to recover gracefully and treat it as a protocol error / connection end.

Example fix

// before
data = await ws.receive_bytes()
// after
from aiohttp import WSMsgType
msg = await ws.receive()
if msg.type == WSMsgType.BINARY:
    data = msg.data
else:
    raise RuntimeError(f"expected binary, got {msg.type}")
Defensive patterns

Strategy: try-catch

Validate before calling

from aiohttp import WSMsgType
async def safe_receive_bytes(ws):
    msg = await ws.receive()
    if msg.type is not WSMsgType.BINARY:
        raise WSMessageTypeError(f"expected BINARY, got {msg.type}")
    return msg.data

Type guard

from aiohttp import WSMsgType
from aiohttp.http_websocket import WSMessage

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

Try / catch

from aiohttp import WSMessageTypeError
try:
    data = await ws.receive_bytes()
except WSMessageTypeError as e:
    if ws.closed:
        return None
    raise

Prevention

When it happens

Trigger: Calling `await ws.receive_bytes()` when the peer sent a TEXT frame, or when the socket returned a CLOSE/ERROR/CLOSED control message.

Common situations: Server normally sends text but client assumed binary. Peer closed the connection mid-exchange. A protocol version mismatch where the server picks text framing. Using receive_bytes() after the connection has already started draining control frames.

Related errors


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