aio-libs/aiohttp · error · RuntimeError

unknown content encoding: {encoding}

Error message

unknown content encoding: {encoding}

What it means

Raised by BodyPartReader._decode_content (the synchronous decode path used by decode()/read(decode=True)) when a part's Content-Encoding header contains a value other than 'identity', 'gzip', or 'deflate'. aiohttp's multipart decoder only knows how to decompress those three encodings via ZLibDecompressor; anything else (brotli, zstd, etc.) is unsupported.

Source

Thrown at aiohttp/multipart.py:580

        """
        data = self._apply_content_transfer_decoding(data)
        if self._needs_content_decoding():
            async for d in self._decode_content_async(data):
                yield d
        else:
            yield data

    def _decode_content(self, data: bytes) -> bytes:
        encoding = self.headers.get(CONTENT_ENCODING, "").lower()
        if encoding == "identity":
            return data
        if encoding in {"deflate", "gzip"}:
            return ZLibDecompressor(
                encoding=encoding,
                suppress_deflate_header=True,
            ).decompress_sync(data, max_length=self._max_decompress_size)

        raise RuntimeError(f"unknown content encoding: {encoding}")

    async def _decode_content_async(self, data: bytes) -> AsyncIterator[bytes]:
        encoding = self.headers.get(CONTENT_ENCODING, "").lower()
        if encoding == "identity":
            yield data
        elif encoding in {"deflate", "gzip"}:
            d = ZLibDecompressor(
                encoding=encoding,
                suppress_deflate_header=True,
            )
            yield await d.decompress(data, max_length=self._max_decompress_size)
            while d.data_available:
                yield await d.decompress(b"", max_length=self._max_decompress_size)
        else:
            raise RuntimeError(f"unknown content encoding: {encoding}")

    def _decode_content_transfer(self, data: bytes) -> bytes:
        encoding = self.headers.get(CONTENT_TRANSFER_ENCODING, "").lower()

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Strip or correct the Content-Encoding on the sender side so only gzip/deflate/identity is used.
  2. If you genuinely need brotli/zstd, decompress the raw bytes yourself after `await part.read(decode=False)` using your own library.
  3. For multipart/form-data, remove Content-Encoding from parts entirely — RFC 7578 forbids it.
  4. Pre-filter part headers before reading if you control the writer.

Example fix

// before
data = await part.read(decode=True)  # Content-Encoding: br -> RuntimeError
// after
raw = await part.read(decode=False)
data = brotli.decompress(raw)  # decompress manually
Defensive patterns

Strategy: validation

Validate before calling

enc = part.headers.get('Content-Encoding', '').lower()
if enc and enc not in ('identity', 'gzip', 'deflate'):
    raise UnsupportedEncoding(f'part uses unsupported Content-Encoding: {enc}')

Type guard

def is_supported_content_encoding(part) -> bool:
    enc = part.headers.get('Content-Encoding', '').lower()
    return enc in ('', 'identity', 'gzip', 'deflate')

Try / catch

try:
    data = await part.read(decode=True)
except RuntimeError as e:
    if 'unknown content encoding' in str(e):
        data = await part.read(decode=False)  # consume raw
    else:
        raise

Prevention

When it happens

Trigger: Receiving a multipart body part whose Content-Encoding header is set to an unsupported algorithm such as 'br' (brotli), 'zstd', 'compress', or a typo like 'gz'. Surfaced when calling `await part.read(decode=True)` or `part.decode(data)` on such a part.

Common situations: A CDN or proxy adds Content-Encoding: br to individual parts; a client compresses with brotli for size; version mismatch where a newer spec introduces an encoding aiohttp does not yet handle; misconfigured sender adding Content-Encoding to a multipart/form-data part (forbidden by RFC 7578 §4.8 but the non-form-data path still checks it).

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/075aa86b9de1d880.json. Report an issue: GitHub.