aio-libs/aiohttp · error · WebSocketError

1007

1007

Error message

Invalid UTF-8 text message

What it means

Raised when a final TEXT frame's payload cannot be decoded as UTF-8. RFC 6455 section 8.1 requires TEXT frames to be valid UTF-8; failure is a protocol error closed with code 1007 (INVALID_TEXT). aiohttp decodes eagerly (unless decode_text=False) in _handle_frame at reader_py.py:272.

Source

Thrown at aiohttp/_websocket/reader_py.py:272

                    ),
                )
                if self._max_msg_size and len(payload_merged) > self._max_msg_size:
                    raise WebSocketError(
                        WSCloseCode.MESSAGE_TOO_BIG,
                        f"Decompressed message exceeds size limit {self._max_msg_size}",
                    )
            elif type(assembled_payload) is bytes:
                payload_merged = assembled_payload
            else:
                payload_merged = bytes(assembled_payload)

            size = len(payload_merged)
            if opcode == OP_CODE_TEXT:
                if self._decode_text:
                    try:
                        text = payload_merged.decode("utf-8")
                    except UnicodeDecodeError as exc:
                        raise WebSocketError(
                            WSCloseCode.INVALID_TEXT, "Invalid UTF-8 text message"
                        ) from exc

                    # XXX: The Text and Binary messages here can be a performance
                    # bottleneck, so we use tuple.__new__ to improve performance.
                    # This is not type safe, but many tests should fail in
                    # test_client_ws_functional.py if this is wrong.
                    msg = TUPLE_NEW(WSMessageText, (text, size, "", WS_MSG_TYPE_TEXT))
                else:
                    # Return raw bytes for TEXT messages when decode_text=False
                    msg = TUPLE_NEW(
                        WSMessageTextBytes, (payload_merged, size, "", WS_MSG_TYPE_TEXT)
                    )
            else:
                msg = TUPLE_NEW(
                    WSMessageBinary, (payload_merged, size, "", WS_MSG_TYPE_BINARY)
                )

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Have the peer send binary data as a BINARY (opcode 0x2) frame instead of TEXT.
  2. Ensure the peer serializes text with UTF-8 specifically.
  3. If you intentionally want raw bytes for TEXT frames, open the WebSocket with decode_text=False and do your own validation.

Example fix

# before
ws = await session.ws_connect(url)  # decodes TEXT as utf-8

# after (opt into raw bytes, validate yourself)
ws = await session.ws_connect(url, decode_text=False)
Defensive patterns

Strategy: validation

Validate before calling

ws = await session.ws_connect(url, decode_text=False)  # get raw bytes; validate yourself
raw = msg.data
try:
    text = raw.decode('utf-8')
except UnicodeDecodeError:
    ...

Try / catch

msg = await ws.receive()
if msg.type == aiohttp.WSMsgType.ERROR and ws.exception().code == 1007:
    await ws.close()

Prevention

When it happens

Trigger: A peer sends a TEXT (opcode 0x1) frame whose bytes contain invalid UTF-8 sequences (e.g. lone surrogates, truncated multibyte chars), and decode_text is True (the default).

Common situations: Peer encodes the message incorrectly (latin-1/cp1252 sent as TEXT), truncation by an intermediary, binary data mislabeled as TEXT, or a fragmented message whose UTF-8 boundary was split across frames and reassembled into invalid bytes.

Related errors


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