encode/httpx · error · ImportError

Using 'BrotliDecoder', but neither of the 'brotlicffi' or…

Error message

Using 'BrotliDecoder', but neither of the 'brotlicffi' or 'brotli' packages have been installed. Make sure to install httpx using `pip install httpx[brotli]`.

What it means

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.

Solutions

  1. Install the extra: `pip install httpx[brotli]` (pulls brotli on CPython, brotlicffi on PyPy).
  2. Alternatively `pip install brotli` (or brotlicffi on PyPy).
  3. Pin the extra in requirements.txt/pyproject: `httpx[brotli]>=0.27`.
  4. If you cannot install, avoid br by sending Accept-Encoding: gzip, deflate (no br) so the server won't brotli-encode.

Example fix

// before
pip install httpx
resp = client.get(url)  # server sends Content-Encoding: br
resp.content  # ImportError
// after
pip install 'httpx[brotli]'
# or, without installing, opt out of br:
resp = client.get(url, headers={'Accept-Encoding': 'gzip, deflate'})
Defensive patterns

Strategy: validation

Validate before calling

# Detect missing brotli before sending
try:
    import brotli  # noqa: F401
    HAS_BROTLI = True
except ImportError:
    HAS_BROTLI = False

headers = {'Accept-Encoding': 'gzip, deflate, br'} if HAS_BROTLI else {'Accept-Encoding': 'gzip, deflate'}
resp = client.get(url, headers=headers)

Type guard

def supports_brotli() -> bool:
    try:
        import brotli  # noqa: F401
        return True
    except ImportError:
        try:
            import brotlicffi  # noqa: F401
            return True
        except ImportError:
            return False

Try / catch

try:
    body = resp.content
except ImportError as exc:
    if 'brotli' in str(exc).lower():
        import subprocess; subprocess.check_call(['pip', 'install', 'httpx[brotli]'])
        # then re-request
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


AI-assisted analysis of encode/httpx@b5addb64f0 (2026-08-04). Data as JSON: /api/errors/b971cf9aa797d96b. Report an issue: GitHub.

Appendix: source

Thrown at httpx/_decoders.py:120

        try:
            return self.decompressor.flush()
        except zlib.error as exc:  # pragma: no cover
            raise DecodingError(str(exc)) from exc


class BrotliDecoder(ContentDecoder):
    """
    Handle 'brotli' decoding.

    Requires `pip install brotlipy`. See: https://brotlipy.readthedocs.io/
        or   `pip install brotli`. See https://github.com/google/brotli
    Supports both 'brotlipy' and 'Brotli' packages since they share an import
    name. The top branches are for 'brotlipy' and bottom branches for 'Brotli'
    """

    def __init__(self) -> None:
        if brotli is None:  # pragma: no cover
            raise ImportError(
                "Using 'BrotliDecoder', but neither of the 'brotlicffi' or 'brotli' "
                "packages have been installed. "
                "Make sure to install httpx using `pip install httpx[brotli]`."
            ) from None

        self.decompressor = brotli.Decompressor()
        self.seen_data = False
        self._decompress: typing.Callable[[bytes], bytes]
        if hasattr(self.decompressor, "decompress"):
            # The 'brotlicffi' package.
            self._decompress = self.decompressor.decompress  # pragma: no cover
        else:
            # The 'brotli' package.
            self._decompress = self.decompressor.process  # pragma: no cover

    def decode(self, data: bytes) -> bytes:
        if not data:
            return b""

View on GitHub (pinned to b5addb64f0)