aio-libs/aiohttp · error · WebSocketError
WSCloseCode.INVALID_TEXT
WSCloseCode.INVALID_TEXT
Error message
Invalid UTF-8 text message
What it means
Raised as WebSocketError(INVALID_TEXT, code 1007) at reader_py.py:272 when a fully-assembled TEXT frame's payload cannot be decoded as UTF-8 (payload_merged.decode('utf-8') raises UnicodeDecodeError). This enforces the RFC 6455 requirement that text frames contain valid UTF-8. Only reached when self._decode_text is True (the default).
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 d9aaf697c2)
Solutions
- Have the peer send non-text data on a binary (opcode 0x2) frame via `ws.send_bytes(data)`.
- If you must tolerate invalid UTF-8, construct WebSocketResponse / ClientWebSocketResponse with autoping=True and handle WSMsgType.ERROR by closing and reconnecting.
- On the receiver, check `msg.type == WSMsgType.ERROR` and inspect `msg.data.code == WSCloseCode.INVALID_TEXT` to report a clear upstream bug.
Example fix
// before (peer side) await ws.send_str(binary_payload) # sent as text, may be non-UTF-8 # after (peer side) await ws.send_bytes(binary_payload) # sent as a binary frame
Defensive patterns
Strategy: try-catch
Validate before calling
# on the SENDER side, ensure text frames carry UTF-8
payload = data.encode('utf-8') if isinstance(data, str) else data
if isinstance(data, (bytes, bytearray)):
await ws.send_bytes(data) # binary frame - safe for non-UTF-8
else:
await ws.send_str(data) # text frame - must be valid UTF-8 Type guard
def is_valid_utf8_text(data: object) -> bool:
if isinstance(data, str):
return True
if isinstance(data, (bytes, bytearray)):
try:
data.decode('utf-8'); return True
except UnicodeDecodeError:
return False
return False Try / catch
msg = await ws.receive()
if msg.type == aiohttp.WSMsgType.ERROR and msg.data.code == aiohttp.WSCloseCode.INVALID_TEXT:
await ws.close()
# peer sent non-UTF-8 on a text frame; report upstream bug Prevention
- Send raw bytes with ws.send_bytes (binary frame), never ws.send_str, to avoid UTF-8 enforcement.
- If decode_text is unnecessary, you may construct the WebSocket with parameters that keep bytes, but still validate the peer.
- Always handle WSMsgType.ERROR + INVALID_TEXT so the connection closes cleanly.
When it happens
Trigger: The remote peer sends a text (opcode 0x1) frame whose assembled payload is not valid UTF-8, e.g. raw binary mislabelled as text, truncated multi-byte sequences, or mixed encodings. Delivered as WSMsgType.ERROR and closes the connection with code 1007.
Common situations: Peer sending bytes/ binary data on a text frame instead of using a binary frame; a producer that concatenates partial UTF-8 chunks across non-spec fragments; legacy/buggy client; a transcoding proxy corrupting bytes.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- WSCloseCode.PROTOCOL_ERROR
- WSCloseCode.PROTOCOL_ERROR
- WSCloseCode.INVALID_TEXT
- Invalid upgrade header
- Invalid connection header
AI-assisted analysis of aio-libs/aiohttp@d9aaf697c2 (2026-08-06).
Data as JSON: /api/errors/a2df6f5338b11972.
Report an issue: GitHub.