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
- Reject the request as 400 Bad Request (this is an HTTP processing error).
- If you legitimately need more headers per part, raise max_headers when constructing the reader: `MultipartReader(headers, content, max_headers=256)`.
- 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
- Keep max_headers at a sane cap; raise it only with justification.
- Reject parts exceeding the limit as 400 (DoS protection).
- Monitor for clients that routinely approach the header cap.
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
- Got more than {limit} bytes when reading: {line!r}.
- Duplicate '{name}' header found.
- Transfer-Encoding can't be present with Content-Length
- Forbidden control character detected in headers. Potential h
- Invalid default charset
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/4481695be120fc5a.json.
Report an issue: GitHub.