{"record":{"id":"05ebeb332de4c6d6","repo":"aio-libs/aiohttp","slug":"wsclosecode-invalid-text-05ebeb","errorCode":"WSCloseCode.INVALID_TEXT","errorMessage":"Invalid UTF-8 text message","messagePattern":"Invalid UTF-8 text message","errorType":"exception","errorClass":"WebSocketError","httpStatus":null,"severity":"error","filePath":"aiohttp/_websocket/reader_c.py","lineNumber":272,"sourceCode":"                    ),\n                )\n                if self._max_msg_size and len(payload_merged) > self._max_msg_size:\n                    raise WebSocketError(\n                        WSCloseCode.MESSAGE_TOO_BIG,\n                        f\"Decompressed message exceeds size limit {self._max_msg_size}\",\n                    )\n            elif type(assembled_payload) is bytes:\n                payload_merged = assembled_payload\n            else:\n                payload_merged = bytes(assembled_payload)\n\n            size = len(payload_merged)\n            if opcode == OP_CODE_TEXT:\n                if self._decode_text:\n                    try:\n                        text = payload_merged.decode(\"utf-8\")\n                    except UnicodeDecodeError as exc:\n                        raise WebSocketError(\n                            WSCloseCode.INVALID_TEXT, \"Invalid UTF-8 text message\"\n                        ) from exc\n\n                    # XXX: The Text and Binary messages here can be a performance\n                    # bottleneck, so we use tuple.__new__ to improve performance.\n                    # This is not type safe, but many tests should fail in\n                    # test_client_ws_functional.py if this is wrong.\n                    msg = TUPLE_NEW(WSMessageText, (text, size, \"\", WS_MSG_TYPE_TEXT))\n                else:\n                    # Return raw bytes for TEXT messages when decode_text=False\n                    msg = TUPLE_NEW(\n                        WSMessageTextBytes, (payload_merged, size, \"\", WS_MSG_TYPE_TEXT)\n                    )\n            else:\n                msg = TUPLE_NEW(\n                    WSMessageBinary, (payload_merged, size, \"\", WS_MSG_TYPE_BINARY)\n                )\n","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d9aaf697c2cd4783ca5749a971965c689f3ec24f/aiohttp/_websocket/reader_c.py#L254-L290","documentation":"Raised when a finalized TEXT frame's payload cannot be decoded as UTF-8. Per RFC 6455 §6.1, the payload of a TEXT message MUST be valid UTF-8; aiohttp enforces this when decode_text=True (the default) by calling bytes.decode('utf-8') and wrapping UnicodeDecodeError as WebSocketError(INVALID_TEXT) at reader_c.py:271-274.","triggerScenarios":"A remote peer sends a TEXT frame (opcode 0x1, possibly assembled from fragments) whose bytes are not valid UTF-8 (lone surrogates, truncated multibyte sequences). The decode at reader_c.py:270 fails and the error is raised.","commonSituations":"A client that sends raw binary data mislabelled as a TEXT frame, a non-compliant library, or a fragmentation edge case where the server splits in the middle of a multibyte character (valid only if the whole message decodes; this fires only on the assembled message).","solutions":["Ensure the remote peer only sends valid UTF-8 in TEXT frames; send binary payloads as BINARY frames instead.","If you must accept raw bytes, open the connection with decode_text=False so TEXT payloads are returned as bytes without validation.","Catch WebSocketError(INVALID_TEXT) in the receive loop and close the connection per spec."],"exampleFix":"// before\nws = await session.ws_connect(url)  # decode_text=True\nasync for msg in ws:\n    print(msg.data)  # str\n// after (accept raw bytes, decode defensively yourself)\nws = await session.ws_connect(url, decode_text=False)\nasync for msg in ws:\n    try:\n        text = msg.data.decode('utf-8')\n    except UnicodeDecodeError:\n        handle_bad_text(msg.data)","handlingStrategy":"try-catch","validationCode":"ws = await session.ws_connect(url, decode_text=False)  # receive TEXT payloads as raw bytes","typeGuard":null,"tryCatchPattern":"from aiohttp import WSCloseCode, WebSocketError\ntry:\n    async for msg in ws:\n        ...\nexcept WebSocketError as exc:\n    if exc.code == WSCloseCode.INVALID_TEXT:\n        await ws.close(code=WSCloseCode.INVALID_TEXT)","preventionTips":["If you tolerate non-UTF-8 TEXT frames, open the connection with decode_text=False and decode defensively yourself.","Send binary payloads as BINARY frames, never mislabelled as TEXT."],"tags":["websocket","invalid-text","utf-8","encoding"],"analyzedSha":"d9aaf697c2cd4783ca5749a971965c689f3ec24f","analyzedAt":"2026-08-06T21:30:48.638Z","schemaVersion":2},"datasetVersion":"2026-08-07T01:17:05.418Z"}