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

  1. Ensure the server emits a complete, properly terminated deflate stream.
  2. Check the connection was not severed before the full body arrived.
  3. 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.
  4. Capture the raw bytes and test with python's zlib to localize the fault.
  5. 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

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


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)