aio-libs/aiohttp · error · WebSocketError
WSCloseCode.MESSAGE_TOO_BIG
WSCloseCode.MESSAGE_TOO_BIG
Error message
Decompressed message exceeds size limit {self._max_msg_size} What it means
Raised as WebSocketError(MESSAGE_TOO_BIG) at reader_py.py:257 after a permessage-deflate decompression when the decompressed payload length exceeds self._max_msg_size. This guards against zip-bomb style attacks where a tiny compressed frame expands past the limit. The decompressor is intentionally allowed to return one extra byte (max_msg_size+1) so that a payload exactly at the boundary is still detectable as over-limit.
Source
Thrown at aiohttp/_websocket/reader_py.py:257
# received.
if compressed:
if not self._decompressobj:
self._decompressobj = ZLibDecompressor(suppress_deflate_header=True)
# XXX: It's possible that the zlib backend (isal is known to
# do this, maybe others too?) will return max_length bytes,
# but internally buffer more data such that the payload is
# >max_length, so we return one extra byte and if we're able
# to do that, then the message is too big.
payload_merged = self._decompressobj.decompress_sync(
assembled_payload + WS_DEFLATE_TRAILING,
(
self._max_msg_size + 1
if self._max_msg_size
else self._max_msg_size
),
)
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
View on GitHub (pinned to d9aaf697c2)
Solutions
- Increase max_msg_size on the receiving side: `ws_connect(url, max_msg_size=16*1024*1024)` or `WebSocketResponse(max_msg_size=...)`.
- If you cannot raise the limit, have the sender split the payload into smaller chunks or avoid permessage-deflate for that message.
- Treat MESSAGE_TOO_BIG as fatal for the session: log, close, and optionally ask the peer to resend smaller frames.
Example fix
// before ws = await session.ws_connect(url) # max_msg_size defaults to 4 MiB # after ws = await session.ws_connect(url, max_msg_size=16 * 1024 * 1024)
Defensive patterns
Strategy: validation
Validate before calling
# raise the cap to match the largest expected compressed-then-decompressed payload MAX = 16 * 1024 * 1024 ws = await session.ws_connect(url, compress=15, max_msg_size=MAX)
Try / catch
msg = await ws.receive()
if msg.type == aiohttp.WSMsgType.ERROR and msg.data.code == aiohttp.WSCloseCode.MESSAGE_TOO_BIG:
await ws.close()
notify_peer_to_chunk() Prevention
- Set max_msg_size large enough for the decompressed payload, not just the compressed wire size.
- Keep max_msg_size symmetric on client and server.
- For unbounded data, have the sender chunk into messages below the limit rather than raising it indefinitely.
When it happens
Trigger: A compressed (permessage-deflate) text/binary message whose decompressed size exceeds the configured max_msg_size. Default max_msg_size is 4 MiB (4*1024*1024) on both client (client.py:944/1019) and server (web_ws.py:109). The connection is closed with code 1009 (MESSAGE_TOO_BIG).
Common situations: Legitimate large payloads (bulk JSON, images) sent compressed when max_msg_size was left at the 4 MiB default; a malicious peer performing a compression amplification / zip-bomb attack; raising max_msg_size on one side but not the other.
Related errors
- Invalid window size
- Extension for deflate not supported{ext.group(1)}
- Compress wbits must between 9 and 15, zlib does not support
- WSCloseCode.MESSAGE_TOO_BIG
- deflate
AI-assisted analysis of aio-libs/aiohttp@d9aaf697c2 (2026-08-06).
Data as JSON: /api/errors/26ea86c5e6ae4210.
Report an issue: GitHub.