aio-libs/aiohttp · error · WebSocketError

1009

1009

Error message

Compressed message has too many deflate members

What it means

While decompressing a `permessage-deflate` message, the zlib backend raised `TooManyMembersError` — the deflate stream contains too many dictionary members / expands without bound. This is a DoS guard against zip-bomb payloads; the reader closes the connection with code 1009 (message too big).

Solutions

  1. If traffic is legitimate, tune compression: negotiate `server_no_context_takeover`/`client_no_context_takeover` to bound decoder state.
  2. Keep/raise `max_msg_size` appropriately; otherwise treat the 1009 as desired protection and drop the connection.
  3. Rate-limit or disconnect repeat offenders at the application layer.
Defensive patterns

Strategy: validation

Validate before calling

# bound decoder state and size during handshake/setup
ws = await session.ws_connect(url, compress=15, max_msg_size=4 * 1024 * 1024)

Type guard

def is_error(msg) -> bool:
    return msg.type == aiohttp.WSMsgType.ERROR

Try / catch

msg = await ws.receive()
if msg.type == aiohttp.WSMsgType.ERROR:
    exc = msg.data            # a WebSocketError
    close_code = exc.code     # 1002/1007/1009/...
    log.warning("ws protocol error %s: %s", close_code, exc)

Prevention

When it happens

Trigger: Peer sends a compressed WS message whose deflate stream is pathological (many members) or expands hugely; a back-end such as isal hits its internal member limit during `decompress_sync`.

Common situations: Malicious client sending a compression bomb; buggy encoder producing a malformed/infinite deflate stream; interop with a peer emitting many deflate blocks.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/0c12668b30b986c1. Report an issue: GitHub.

Appendix: source

Thrown at aiohttp/_websocket/reader_c.py:261

            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.
                try:
                    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
                        ),
                    )
                except TooManyMembersError as exc:
                    raise WebSocketError(
                        WSCloseCode.MESSAGE_TOO_BIG,
                        "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")

View on GitHub (pinned to d041d4d0fd)