aio-libs/aiohttp · error · ContentEncodingError

Can not decode content-encoding: brotli (br). Please…

Error message

Can not decode content-encoding: brotli (br). Please install `Brotli`

What it means

Raised as ContentEncodingError when a response body declares 'Content-Encoding: br' (brotli) but the optional Brotli library is not installed (HAS_BROTLI is False). DeflateBuffer.__init__ checks the flag before constructing a BrotliDecompressor and raises this actionable install hint. The error surfaces when the body is being set up for decompression.

Solutions

  1. Install Brotli: 'pip install Brotli' (the message names this exact package).
  2. Or install aiohttp with the brotli extra: 'pip install "aiohttp[brotli]"'.
  3. If you cannot install it, disable auto-decompression: ClientSession(..., auto_decompress=False) and decompress manually / or ask the server not to send br.
  4. Strip 'br' from your Accept-Encoding request header so the server does not use brotli.

Example fix

# before
pip install aiohttp
session = aiohttp.ClientSession()  # server sends br -> error

# after
pip install Brotli
# or
session = aiohttp.ClientSession(headers={'Accept-Encoding': 'gzip, deflate'})
Defensive patterns

Strategy: fallback

Validate before calling

try:
    import brotli  # noqa: F401
    HAS_BROTLI = True
except ImportError:
    HAS_BROTLI = False

def accept_encoding_for_brotli(want_brotli: bool = True) -> str:
    base = 'gzip, deflate'
    if want_brotli and HAS_BROTLI:
        return base + ', br'
    return base

Try / catch

from aiohttp.http_exceptions import ContentEncodingError
import aiohttp

async def get_robust(url):
    try:
        async with aiohttp.ClientSession() as s:
            async with s.get(url) as r:
                return await r.read()
    except ContentEncodingError as e:
        if 'brotli' in str(e):
            # retry without advertising br
            async with aiohttp.ClientSession(headers={'Accept-Encoding': 'gzip, deflate'}) as s:
                async with s.get(url) as r:
                    return await r.read()
        raise

Prevention

When it happens

Trigger: The server returns 'Content-Encoding: br' and auto_decompress is enabled (default), but the brotli (or Brotli) package is not importable in the aiohttp compression_utils module, so HAS_BROTLI is False.

Common situations: A fresh aiohttp install in a minimal environment (no brotli); a Docker image / slim deployment that omitted optional deps; CI that installs only 'aiohttp' without '[speedups]' or the brotli extra; a production env where brotli was available at build time but removed; servers that start sending brotli after an upstream change.

Related errors


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

Appendix: source

Thrown at aiohttp/http_parser.py:1149

class DeflateBuffer:
    """DeflateStream decompress stream and feed data into specified stream."""

    def __init__(
        self,
        out: StreamReader,
        encoding: str | None,
        max_decompress_size: int = DEFAULT_CHUNK_SIZE,
    ) -> None:
        self.out = out
        self.size = 0
        out.total_compressed_bytes = self.size
        self.encoding = encoding
        self._started_decoding = False

        self.decompressor: BrotliDecompressor | ZLibDecompressor | ZSTDDecompressor
        if encoding == "br":
            if not HAS_BROTLI:
                raise ContentEncodingError(
                    "Can not decode content-encoding: brotli (br). "
                    "Please install `Brotli`"
                )
            self.decompressor = BrotliDecompressor()
        elif encoding == "zstd":
            if not HAS_ZSTD:
                raise ContentEncodingError(
                    "Can not decode content-encoding: zstandard (zstd). "
                    "Please install `backports.zstd`"
                )
            self.decompressor = ZSTDDecompressor()
        else:
            self.decompressor = ZLibDecompressor(encoding=encoding)

        self._max_decompress_size = max_decompress_size

    def set_exception(
        self,

View on GitHub (pinned to d041d4d0fd)