{"record":{"id":"7bcfb15342f0363d","repo":"aio-libs/aiohttp","slug":"compress-wbits-must-between-9-and-15-zlib-does-no","errorCode":null,"errorMessage":"Compress wbits must between 9 and 15, zlib does not support wbits=8","messagePattern":"Compress wbits must between 9 and 15, zlib does not support wbits=8","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/_websocket/helpers.py","lineNumber":135,"sourceCode":"                        raise WSHandshakeError(\"Invalid window size\")\n                if match.group(2):\n                    notakeover = True\n                # Ignore regex group 5 & 6 for client_max_window_bits\n                break\n        # Return Fail if client side and not match\n        elif not isserver:\n            raise WSHandshakeError(\"Extension for deflate not supported\" + ext.group(1))\n\n    return compress, notakeover\n\n\ndef ws_ext_gen(\n    compress: int = 15, isserver: bool = False, server_notakeover: bool = False\n) -> str:\n    # client_notakeover=False not used for server\n    # compress wbit 8 does not support in zlib\n    if compress < 9 or compress > 15:\n        raise ValueError(\n            \"Compress wbits must between 9 and 15, zlib does not support wbits=8\"\n        )\n    enabledext = [\"permessage-deflate\"]\n    if not isserver:\n        enabledext.append(\"client_max_window_bits\")\n\n    if compress < 15:\n        enabledext.append(\"server_max_window_bits=\" + str(compress))\n    if server_notakeover:\n        enabledext.append(\"server_no_context_takeover\")\n    # if client_notakeover:\n    #     enabledext.append('client_no_context_takeover')\n    return \"; \".join(enabledext)\n","sourceCodeStart":117,"sourceCodeEnd":149,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/_websocket/helpers.py#L117-L149","documentation":"`ws_ext_gen` builds the `Sec-WebSocket-Extensions` offer string and requires `compress` (the window-bits argument) to be 9-15, because zlib does not support wbits=8. Passing any other value is a programmer error that raises `ValueError` immediately — it is never produced by network input.","triggerScenarios":"Calling `ws_ext_gen(compress=8)`, `ws_ext_gen(compress=16)`, or `ws_ext_gen(compress=0)` directly; or forwarding an unchecked config/env value into an API that reaches `ws_ext_gen` (note: aiohttp's own server path only passes 0 or 9-15 from `ws_ext_parse`, so this fires for direct/custom callers).","commonSituations":"App reads a compression level from configuration and forwards it without bounds-checking; developer confuses zlib's general wbits (which permits 8 in raw mode) with the WS deflate range; passing 0 meaning 'no compression' into `ws_ext_gen` instead of disabling compression at a higher level.","solutions":["Pass `compress` in the range 9-15 (15 is the default and most common).","Clamp/validate the value before calling: `compress = max(9, min(15, compress))` when it is config-driven.","To disable compression, do not call `ws_ext_gen` — set `compress=0`/`compress=False` on `ws_connect`/`WebSocketResponse`."],"exampleFix":"# before\nhdr = aiohttp.ws_ext_gen(compress=8)   # ValueError\n# after\nhdr = aiohttp.ws_ext_gen(compress=15)   # valid","handlingStrategy":"validation","validationCode":"if not (9 <= compress <= 15):\n    raise ValueError(f\"compress must be 9-15, got {compress}\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate config-driven compression values at startup, not at request time.","Remember the WS deflate range (9-15) differs from raw zlib's (0-15)."],"tags":["websocket","compression","validation","deflate","value-error"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}