aio-libs/aiohttp · error · WebSocketError
1007
1007
Error message
Invalid UTF-8 text message
What it means
Raised as WebSocketError code 1007 (INVALID_TEXT) when a TEXT frame's assembled payload fails UTF-8 decoding (payload_merged.decode('utf-8') raises UnicodeDecodeError). RFC 6455 §8.1 mandates that the connection be closed with code 1007 when a text message contains invalid UTF-8. This path only runs when self._decode_text is True (the default); with decode_text=False the raw bytes are returned and no validation occurs here.
Solutions
- Ensure the peer only sends valid UTF-8 in TEXT frames; use BINARY frames (opcode 0x2) for non-text payloads.
- If you are the sender, encode with a strict UTF-8 encoder (data.encode('utf-8')) and never split a multibyte character across fragments.
- On the receiver side, catch WebSocketError and close with code 1007.
- If you intentionally exchange raw bytes, use BINARY frames or set decode_text=False on the reader (Python-only, bypasses validation).
Example fix
// before: sender labels binary as text
ws.send_str(b'\xff\xfe ...') // not valid utf-8
// after: use bytes via send_bytes / BINARY
ws.send_bytes(raw_payload)
// or ensure utf-8 on the text path
ws.send_str(text.decode('utf-8','replace') if unsure else text) Defensive patterns
Strategy: try-catch
Validate before calling
// Sender: ensure strict UTF-8 before sending text.
try:
text.encode('utf-8') # validates on the send side
except UnicodeEncodeError:
await ws.send_bytes(payload) # use BINARY instead
Type guard
def is_valid_utf8(b: bytes) -> bool:
try:
b.decode('utf-8'); return True
except UnicodeDecodeError:
return False
Try / catch
try:
msg = await ws.receive()
except WebSocketError as exc:
if exc.code == WSCloseCode.INVALID_TEXT:
await ws.close(code=WSCloseCode.INVALID_TEXT)
Prevention
- Use BINARY frames for non-text payloads.
- Encode all TEXT payloads with .encode('utf-8') and never split multibyte characters across fragments.
- For raw-byte text channels, set decode_text=False deliberately and document why.
When it happens
Trigger: A peer sends a TEXT frame (opcode 0x1) whose payload is not valid UTF-8 — e.g. a truncated multibyte sequence, lone surrogate bytes, or binary accidentally labelled as text. _handle_frame assembles the payload and calls .decode('utf-8'), which throws.
Common situations: A client serializes binary data into a TEXT frame by mistake; a frame is truncated by a buggy intermediary or network layer cutting a multibyte character; a custom encoder producing non-UTF-8 bytes; partial/fragmented text whose split point lands inside a multibyte sequence at the application layer (the reader reassembles correctly, but a hand-built sender can split badly).
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
- 1007
- 1002
- 1002
- Compress wbits must between 9 and 15, zlib does not support…
- data argument must be str (%r)
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/51d6233dfaa8735b.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/_websocket/reader_py.py:281
"Compressed message has too many deflate members",
) from exc
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 d041d4d0fd)