aio-libs/aiohttp · error · WebSocketError
1007
1007
Error message
Invalid UTF-8 text message
What it means
WebSocketError (1007 INVALID_TEXT) raised in _handle_frame when a TEXT message payload fails to decode as UTF-8 (decode_text is True). RFC 6455 requires TEXT frames to be valid UTF-8; aiohttp validates this and rejects invalid payloads. Surfaced as msg.type == WSMsgType.ERROR with msg.data.code == 1007.
Source
Thrown at aiohttp/_websocket/reader_c.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
- Ensure the sender only sends valid UTF-8 in TEXT frames; use BINARY frames (send_bytes) for non-text payloads.
- If you can tolerate raw bytes, construct the server/client with decode_text=False so TEXT payloads are returned as bytes and 1007 is not raised for them.
- Handle msg.type == WSMsgType.ERROR / code 1007 in the receive loop and close/reconnect.
- On the sender, encode explicitly: payload.encode('utf-8') before framing.
Example fix
# before ws = WebSocketResponse(decode_text=True) # invalid UTF-8 TEXT -> 1007 # after (option A: accept raw bytes) ws = WebSocketResponse(decode_text=False) # option B: peer must send BINARY for non-utf8 data: await ws.send_bytes(data)
Defensive patterns
Strategy: type-guard
Validate before calling
# if non-UTF-8 TEXT is expected, receive raw bytes instead of decoding ws = WebSocketResponse(decode_text=False) # server side # ws = await session.ws_connect(url, decode_text=False) # client side (if supported)
Type guard
from aiohttp import WSMsgType
def is_invalid_text(msg) -> bool:
return msg.type is WSMsgType.ERROR and getattr(msg.data, 'code', None) == 1007 Try / catch
msg = await ws.receive()
if msg.type is aiohttp.WSMsgType.ERROR and msg.data.code == 1007:
log.warning('peer sent invalid UTF-8 TEXT frame')
await ws.close() Prevention
- Send non-text payloads as BINARY (send_bytes), never as TEXT.
- If you must accept possibly-invalid text, construct the endpoint with decode_text=False.
- Encode TEXT payloads explicitly with .encode('utf-8') on the sender.
When it happens
Trigger: A frame with opcode 0x1 (TEXT) whose assembled payload contains invalid UTF-8 byte sequences, decoded at reader_c.py:270. Triggered by a peer that sends binary data mislabelled as a TEXT frame, or a partial UTF-8 sequence split across fragments that was reassembled incorrectly by the sender.
Common situations: A peer encoding text in latin-1/GBK/Shift-JIS but sending as TEXT; binary data accidentally sent via send_str() on the peer; a fragmenting sender that splits a multi-byte UTF-8 character incorrectly; legacy/buggy clients.
Related errors
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/d717194b31f376eb.json.
Report an issue: GitHub.