aio-libs/aiohttp · error · WSMessageTypeError

Received message : is not WSMsgType.BINARY

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 message returned by receive() is not WSMsgType.BINARY. receive_bytes() expects a binary frame; receiving a TEXT frame, or any control/closing frame, trips the guard. The %r formatting shows the offending frame type and data.

Solutions

  1. Use receive() and dispatch on msg.type so TEXT, BINARY, CLOSE, and ERROR are handled distinctly.
  2. Catch WSMessageTypeError and either switch to text handling or close with a type-mismatch code.
  3. Align the client to send BINARY frames (e.g. orjson.dumps which returns bytes).

Example fix

// before
payload = await ws.receive_bytes()  # raises on text

// after
from aiohttp import WSMessageTypeError
try:
    payload = await ws.receive_bytes()
except WSMessageTypeError:
    msg = await ws.receive()
    if msg.type == WSMsgType.TEXT:
        payload = msg.data.encode('utf-8')
    else:
        raise
# or dispatch up front:
msg = await ws.receive()
if msg.type == WSMsgType.BINARY: ...
elif msg.type == WSMsgType.TEXT: ...
Defensive patterns

Strategy: try-catch

Validate before calling

from aiohttp import WSMessageTypeError
try:
    payload = await ws.receive_bytes()
except WSMessageTypeError:
    msg = await ws.receive()
    if msg.type == WSMsgType.BINARY:
        payload = msg.data
    else:
        raise

Type guard

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

Try / catch

try:
    payload = await ws.receive_bytes()
except WSMessageTypeError as e:
    await ws.close(code=WSCloseCode.UNSUPPORTED_DATA, message=b'expected binary')
    raise

Prevention

When it happens

Trigger: Peer sends a TEXT frame when the server calls receive_bytes(); a CLOSE/CLOSED frame arrives during a receive_bytes loop; the client serializes with json.dumps (str) but the server expects bytes.

Common situations: Mismatched framing between client (text JSON) and server (expecting binary); control frames interrupting a binary read loop; reusing receive_bytes when the protocol actually sends text.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/4a4a83ac04722223. Report an issue: GitHub.

Appendix: 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 d041d4d0fd)