aio-libs/aiohttp · error · BadHttpMessage

Too many headers received

Error message

Too many headers received

What it means

Raised by MultipartReader._read_headers() when a single multipart part contains more header lines than max_headers (default 128). This is a hard cap to prevent unbounded memory/CPU consumption from a part with a header bomb. It raises BadHttpMessage (an HTTP processing error), not a plain ValueError.

Source

Thrown at aiohttp/multipart.py:890

                self._unread.append(next_line)
            # otherwise the request is likely missing an epilogue and both
            # lines should be passed to the parent for processing
            # (this handles the old behavior gracefully)
            else:
                self._unread.extend([next_line, epilogue])
        else:
            raise ValueError(f"Invalid boundary {chunk!r}, expected {self._boundary!r}")

    async def _read_headers(self) -> HeadersDictProxy:
        lines = []
        while True:
            chunk = await self._content.readline(max_line_length=self._max_field_size)
            chunk = chunk.rstrip(b"\r\n")
            lines.append(chunk)
            if not chunk:
                break
            if len(lines) > self._max_headers:
                raise BadHttpMessage("Too many headers received")
        parser = HeadersParser(max_field_size=self._max_field_size)
        headers, _ = parser.parse_headers(lines)
        return headers

    async def _maybe_release_last_part(self) -> None:
        """Ensures that the last read body part is read completely."""
        if self._last_part is not None:
            if not self._last_part.at_eof():
                await self._last_part.release()
            self._unread.extend(self._last_part._unread)
            self._last_part = None


_Part = tuple[Payload, str, str]


class MultipartWriter(Payload):
    """Multipart body writer."""

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Reject the request as 400 Bad Request (this is an HTTP processing error).
  2. If you legitimately need more headers per part, raise max_headers when constructing the reader: `MultipartReader(headers, content, max_headers=256)`.
  3. Investigate the sender — >128 headers in one part is almost always a bug.

Example fix

// before
reader = await request.multipart()  # part with 200 headers -> BadHttpMessage
// after
reader = MultipartReader(
    request.headers, request.content, max_headers=256
)
Defensive patterns

Strategy: try-catch

Validate before calling

from aiohttp.http_exceptions import BadHttpMessage
# pre-check is impractical (headers not yet read); configure the cap explicitly:
reader = MultipartReader(headers, content, max_headers=256)

Try / catch

from aiohttp.http_exceptions import BadHttpMessage
try:
    async for part in reader:
        process(part)
except BadHttpMessage as e:
    if 'Too many headers' in str(e):
        return web.Response(status=400, text='header limit exceeded')
    raise

Prevention

When it happens

Trigger: Receiving a multipart part whose header block exceeds 128 lines (configurable via the MultipartReader max_headers kwarg). Triggered during fetch_next_part() while reading the next part's headers.

Common situations: Malicious clients sending header bombs; buggy generators emitting duplicate headers in a loop; legitimate use with a very large number of custom metadata headers exceeding the default.

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/4481695be120fc5a.json. Report an issue: GitHub.