{"id":"b971cf9aa797d96b","repo":"encode/httpx","slug":"using-brotlidecoder-but-neither-of-the-brotlic","errorCode":null,"errorMessage":"Using 'BrotliDecoder', but neither of the 'brotlicffi' or 'brotli' packages have been installed. Make sure to install httpx using `pip install httpx[brotli]`.","messagePattern":"Using 'BrotliDecoder', but neither of the 'brotlicffi' or 'brotli' packages have been installed\\. Make sure to install httpx using `pip install httpx\\[brotli\\]`\\.","errorType":"exception","errorClass":"ImportError","httpStatus":null,"severity":"error","filePath":"httpx/_decoders.py","lineNumber":120,"sourceCode":"        try:\n            return self.decompressor.flush()\n        except zlib.error as exc:  # pragma: no cover\n            raise DecodingError(str(exc)) from exc\n\n\nclass BrotliDecoder(ContentDecoder):\n    \"\"\"\n    Handle 'brotli' decoding.\n\n    Requires `pip install brotlipy`. See: https://brotlipy.readthedocs.io/\n        or   `pip install brotli`. See https://github.com/google/brotli\n    Supports both 'brotlipy' and 'Brotli' packages since they share an import\n    name. The top branches are for 'brotlipy' and bottom branches for 'Brotli'\n    \"\"\"\n\n    def __init__(self) -> None:\n        if brotli is None:  # pragma: no cover\n            raise ImportError(\n                \"Using 'BrotliDecoder', but neither of the 'brotlicffi' or 'brotli' \"\n                \"packages have been installed. \"\n                \"Make sure to install httpx using `pip install httpx[brotli]`.\"\n            ) from None\n\n        self.decompressor = brotli.Decompressor()\n        self.seen_data = False\n        self._decompress: typing.Callable[[bytes], bytes]\n        if hasattr(self.decompressor, \"decompress\"):\n            # The 'brotlicffi' package.\n            self._decompress = self.decompressor.decompress  # pragma: no cover\n        else:\n            # The 'brotli' package.\n            self._decompress = self.decompressor.process  # pragma: no cover\n\n    def decode(self, data: bytes) -> bytes:\n        if not data:\n            return b\"\"","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/encode/httpx/blob/b5addb64f0161ff6bfe94c124ef76f6a1fba5254/httpx/_decoders.py#L102-L138","documentation":"ImportError raised in BrotliDecoder.__init__ when neither the brotli nor brotlicffi package is importable but a response arrives with 'Content-Encoding: br'. httpx lazily registers the 'br' decoder only if a brotli library is present at import time (SUPPORTED_DECODERS.pop('br') otherwise), so this firing means the library became available at decoder-construction time inconsistently, or the decoder was constructed manually. The fix is to install the optional brotli extra.","triggerScenarios":"Response with 'Content-Encoding: br' when httpx was installed without the brotli extra; manually instantiating BrotliDecoder() in tests; environment where brotli was uninstalled/locked out after httpx imported; CI image missing the C brotli library.","commonSituations":"Fresh pip install httpx without extras hitting a brotli-compressed CDN (Cloudflare, Google often send br); deploying to a slim Docker image; upgrading Python without reinstalling binary wheels; dev-vs-prod dependency mismatch (extras only in requirements-dev).","solutions":["Install the extra: `pip install httpx[brotli]` (pulls brotli on CPython, brotlicffi on PyPy).","Alternatively `pip install brotli` (or brotlicffi on PyPy).","Pin the extra in requirements.txt/pyproject: `httpx[brotli]>=0.27`.","If you cannot install, avoid br by sending Accept-Encoding: gzip, deflate (no br) so the server won't brotli-encode."],"exampleFix":"// before\npip install httpx\nresp = client.get(url)  # server sends Content-Encoding: br\nresp.content  # ImportError\n// after\npip install 'httpx[brotli]'\n# or, without installing, opt out of br:\nresp = client.get(url, headers={'Accept-Encoding': 'gzip, deflate'})","handlingStrategy":"validation","validationCode":"# Detect missing brotli before sending\ntry:\n    import brotli  # noqa: F401\n    HAS_BROTLI = True\nexcept ImportError:\n    HAS_BROTLI = False\n\nheaders = {'Accept-Encoding': 'gzip, deflate, br'} if HAS_BROTLI else {'Accept-Encoding': 'gzip, deflate'}\nresp = client.get(url, headers=headers)","typeGuard":"def supports_brotli() -> bool:\n    try:\n        import brotli  # noqa: F401\n        return True\n    except ImportError:\n        try:\n            import brotlicffi  # noqa: F401\n            return True\n        except ImportError:\n            return False","tryCatchPattern":"try:\n    body = resp.content\nexcept ImportError as exc:\n    if 'brotli' in str(exc).lower():\n        import subprocess; subprocess.check_call(['pip', 'install', 'httpx[brotli]'])\n        # then re-request\n    else:\n        raise","preventionTips":["Install httpx with extras: pip install 'httpx[brotli,zstd]'.","Declare extras in pyproject/requirements, not just plain httpx.","Match Accept-Encoding to actually-installed codecs.","Pin dependency versions to avoid wheel regressions."],"tags":["brotli","dependencies","content-encoding","environment"],"analyzedSha":"b5addb64f0161ff6bfe94c124ef76f6a1fba5254","analyzedAt":"2026-08-04T19:32:56.768Z","schemaVersion":2}