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 receive_bytes() when the next message is not WSMsgType.BINARY. It is the binary counterpart of error 82: receive_bytes() assumes the next frame is BINARY and rejects TEXT, CLOSE, PING, PONG, or ERROR frames with WSMessageTypeError.

Solutions

  1. Drive a ws.receive() loop and branch on msg.type before decoding.
  2. If text frames are legitimate, call receive_str() for those or fall back to receive().
  3. Break the loop cleanly on CLOSE/CLOSED instead of calling receive_bytes() again.
  4. Catch WSMessageTypeError to survive unexpected text frames during binary streaming.

Example fix

# before
async for _ in range(10):
    payload = await ws.receive_bytes()
# after
while True:
    msg = await ws.receive()
    if msg.type is WSMsgType.BINARY:
        payload = msg.data
    elif msg.type is WSMsgType.TEXT:
        log.warning('unexpected text: %r', msg.data)
    elif msg.type in (WSMsgType.CLOSED, WSMsgType.CLOSE):
        break
Defensive patterns

Strategy: validation

Validate before calling

async def receive_binary(ws, timeout=None):
    msg = await ws.receive(timeout)
    if msg.type is aiohttp.WSMsgType.BINARY:
        return msg.data
    if msg.type in (aiohttp.WSMsgType.CLOSED, aiohttp.WSMsgType.CLOSE):
        raise ConnectionResetError('websocket closed')
    return None

Type guard

from aiohttp import WSMsgType

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

Try / catch

from aiohttp import WSMessageTypeError
try:
    payload = await ws.receive_bytes()
except WSMessageTypeError as exc:
    log.warning('non-binary frame ignored: %s', exc)
    payload = b''

Prevention

When it happens

Trigger: Server sends a TEXT frame (e.g. an error string) while client loops on receive_bytes(); a CLOSE frame arrives mid-stream; PING/PONG control frames are queued ahead of the next binary payload.

Common situations: Mixed-protocol servers that interleave text status messages with binary data; client assuming pure-binary when the server sends a textual handshake/error; teardown ordering.

Related errors


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

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