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
- Restrict Content-Encoding on multipart parts to identity, deflate, or gzip.
- If you need Brotli/Zstandard, pre-decode the bytes yourself before wrapping them in a part, or register a custom decode step.
- Strip the unsupported Content-Encoding header and decompress externally.
- 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
- Restrict part-level Content-Encoding to identity/deflate/gzip.
- Pre-decode unsupported encodings (br/zstd) before constructing the part.
- Validate headers before forwarding or parsing.
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
- unknown content transfer encoding
- 1009
- boundary missed for Content-Type
- boundary %r is too long (70 chars max)
- boundary should contain ASCII only chars
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)