aio-libs/aiohttp · error · ContentEncodingError
deflate
Error message
deflate
What it means
Raised as ContentEncodingError (message 'deflate') at EOF when a deflate-compressed body did not finish cleanly — specifically when encoding is 'deflate' and self.decompressor.eof is False after flush() in feed_eof. The flush() is asserted to return no data; if the zlib stream did not reach its end marker, this fires, signalling a truncated or malformed deflate stream.
Solutions
- Ensure the server emits a complete, properly terminated deflate stream.
- Check the connection was not severed before the full body arrived.
- Verify whether the body is zlib-wrapped (RFC 1950) or raw deflate (RFC 1951) — the parser tries to auto-detect via the first byte; mismatches can still partially decode then fail at EOF.
- Capture the raw bytes and test with python's zlib to localize the fault.
- If the body is intentionally raw, confirm the server emits the correct format.
Example fix
# before (server sends incomplete deflate)
resp = web.Response(body=truncated_raw_deflate, headers={'Content-Encoding': 'deflate'})
# after (send a complete zlib stream)
import zlib
compressed = zlib.compress(data, wbits=15) # zlib-wrapped deflate
resp = web.Response(body=compressed, headers={'Content-Encoding': 'deflate'}) Defensive patterns
Strategy: try-catch
Validate before calling
import zlib
def validate_deflate_stream(raw: bytes) -> bool:
# try zlib-wrapped first, then raw deflate
for wbits in (15, -15):
try:
d = zlib.decompressobj(wbits)
d.decompress(raw)
d.flush()
if d.eof:
return True
except zlib.error:
continue
return False Try / catch
from aiohttp.http_exceptions import ContentEncodingError
import aiohttp
async def get_deflate_safe(url):
try:
async with aiohttp.ClientSession() as s:
async with s.get(url) as r:
return await r.read()
except ContentEncodingError as e:
if str(e) == 'deflate':
# server may send raw vs zlib-wrapped; fetch raw and decode manually
async with aiohttp.ClientSession(auto_decompress=False) as s:
async with s.get(url) as r:
raw = await r.read()
import zlib
try:
return zlib.decompress(raw, -15)
except zlib.error:
return zlib.decompress(raw, 15)
raise Prevention
- Ensure servers emit a complete, terminated deflate stream.
- Confirm zlib-wrapped (RFC 1950) vs raw (RFC 1951) consistency with the header.
- Check the connection is not dropping the tail.
- Validate with python's zlib before blaming the client.
When it happens
Trigger: A response with 'Content-Encoding: deflate' where the compressed stream is incomplete or corrupt such that the ZLibDecompressor.eof is False at EOF. Triggers in DeflateBuffer.feed_eof when self.size > 0 and the deflate decompressor has not seen its end marker.
Common situations: Server sends a truncated deflate stream (connection dropped mid-body); a server computing deflate incorrectly; a proxy stripping trailing bytes; raw deflate vs zlib-wrapped deflate mismatch that initially decoded but did not terminate; clients that abort reading before completion.
Related errors
- Can not decode content-encoding
- Can not decode content-encoding: brotli (br). Please…
- Can not decode content-encoding: zstandard (zstd). Please…
- 1009
- Compress wbits must between 9 and 15, zlib does not support…
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/4b5f1fc1ef80dc88.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/http_parser.py:1218
raise ContentEncodingError(
"Can not decode content-encoding: %s" % self.encoding
)
if chunk:
self.out.feed_data(chunk)
return self.decompressor.data_available
def feed_eof(self) -> None:
chunk = self.decompressor.flush()
# This should never contain data as we defer the call until exhausting
# the decompression. If .flush() is returning data, this may indicate a
# zip bomb vulnerability as it will decompress all remaining data at once.
assert not chunk
if self.size > 0:
# decompressor is not brotli unless encoding is "br"
if self.encoding == "deflate" and not self.decompressor.eof: # type: ignore[union-attr]
raise ContentEncodingError("deflate")
self.out.feed_eof()
def begin_http_chunk_receiving(self) -> None:
self.out.begin_http_chunk_receiving()
def end_http_chunk_receiving(self) -> None:
self.out.end_http_chunk_receiving()
HttpRequestParserPy = HttpRequestParser
HttpResponseParserPy = HttpResponseParser
RawRequestMessagePy = RawRequestMessage
RawResponseMessagePy = RawResponseMessage
with suppress(ImportError):
if not NO_EXTENSIONS:
from ._http_parser import ( # type: ignore[import-not-found,no-redef]View on GitHub (pinned to d041d4d0fd)