aio-libs/aiohttp · error · RuntimeError

unknown content encoding

Error message

unknown content encoding: {encoding}

What it means

Raised by the synchronous _decode_content when a body part's Content-Encoding header is neither 'identity' nor one of the supported 'deflate'/'gzip'. Only those three encodings are implemented via ZLibDecompressor.

Solutions

  1. Restrict Content-Encoding on multipart parts to identity, deflate, or gzip.
  2. If you need Brotli/Zstandard, pre-decode the bytes yourself before wrapping them in a part, or register a custom decode step.
  3. Strip the unsupported Content-Encoding header and decompress externally.
  4. Catch RuntimeError at the decode site and fall back to raw bytes.

Example fix

// before
Content-Encoding: br

// after
Content-Encoding: gzip
Defensive patterns

Strategy: validation

Validate before calling

SUPPORTED_CONTENT_ENCODING = {'identity', 'deflate', 'gzip'}

def assert_supported_encoding(headers):
    ce = headers.get('Content-Encoding', '').lower()
    if ce and ce not in SUPPORTED_CONTENT_ENCODING:
        raise ValueError(f'Unsupported Content-Encoding for sync decode: {ce}')

Type guard

def is_supported_content_encoding(value: object) -> bool:
    return isinstance(value, str) and value.lower() in {'identity', 'deflate', 'gzip'}

Try / catch

try:
    decoded = part._decode_content(data)
except RuntimeError as e:
    if 'unknown content encoding' in str(e):
        decoded = data  # or hand off to a custom decoder
    else:
        raise

Prevention

When it happens

Trigger: A part with Content-Encoding set to 'br', 'compress', 'zstd', 'x-gzip' (misspelled), or any other token; the sync decode path is hit when content is decoded in batch.

Common situations: A peer advertising modern encodings (Brotli/Zstandard) that aiohttp's multipart decoder does not handle; typos in Content-Encoding; custom encoding tokens.

Related errors


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

Appendix: 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 d041d4d0fd)