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
- Strip or correct the Content-Encoding on the sender side so only gzip/deflate/identity is used.
- If you genuinely need brotli/zstd, decompress the raw bytes yourself after `await part.read(decode=False)` using your own library.
- For multipart/form-data, remove Content-Encoding from parts entirely — RFC 7578 forbids it.
- 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
- Inspect each part's Content-Encoding before calling read(decode=True).
- Strip unsupported Content-Encoding headers at a proxy before they reach aiohttp.
- Document supported encodings to upstream teams to avoid brotli/zstd on multipart parts.
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
- unknown content transfer encoding: {encoding}
- Invalid window size
- Extension for deflate not supported{ext}
- Compress wbits must between 9 and 15, zlib does not support
- 1009
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/075aa86b9de1d880.json.
Report an issue: GitHub.