aio-libs/aiohttp · error · BadHttpMessage
Too many trailers received
Error message
Too many trailers received
What it means
Raised as BadHttpMessage (HTTP 400) when the number of trailer lines in a chunked body exceeds _max_trailers (default 128, derived from max_headers). After each trailer line is appended, the parser checks 'if len(self._trailer_lines) > self._max_trailers' and raises. This bounds the count of trailer headers to prevent resource exhaustion.
Solutions
- Limit the number of trailer headers you send (<=128 by default).
- If more trailers are genuinely needed, raise max_headers on the parser (ClientSession(..., max_headers=N) or RequestHandler kwargs).
- Strip unnecessary trailers at proxies before forwarding.
- Treat unbounded trailer counts from untrusted clients as malicious.
Example fix
# before session = aiohttp.ClientSession() # after (raise trailer/header cap if needed) session = aiohttp.ClientSession(max_headers=256)
Defensive patterns
Strategy: validation
Validate before calling
def trailer_count_ok(trailers: list, max_trailers: int = 128) -> bool:
return len(trailers) <= max_trailers Try / catch
from aiohttp.http_exceptions import BadHttpMessage
try:
parser.feed_data(raw)
except BadHttpMessage as e:
respond_400(str(e)) # 'Too many trailers received' Prevention
- Limit trailer count to <= max_headers (default 128).
- Raise max_headers on the parser if more trailers are required.
- Strip unnecessary trailers at proxies.
- Treat excessive trailers from untrusted clients as an attack.
When it happens
Trigger: A chunked body whose trailer section contains more than max_trailers (default 128) lines before the terminating empty line. Fires inside PARSE_TRAILERS as lines accumulate.
Common situations: An attacker flooding the trailer section with many headers (resource-exhaustion); a misbehaving intermediary appending per-hop trailer headers; a server/client streaming many trailer headers by mistake; legitimate high trailer counts exceeding the default 128.
Related errors
- Not enough data to satisfy transfer length header.
- Request has duplicate `chunked` Transfer-Encoding
- Request has invalid `Transfer-Encoding`
- chunked can not be set if Content-Length header is set
- chunked can not be set if "Transfer-Encoding: chunked"…
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/e7dc29c1627cea94.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/http_parser.py:1098
"Bad trailer line ending, expected CRLF"
)
set_exception(self.payload, exc)
raise exc
self._chunk_tail = chunk
return PayloadState.PAYLOAD_NEEDS_INPUT, b""
line = chunk[:pos]
chunk = chunk[pos + len(SEP) :]
if SEP == b"\n": # For lax response parsing
line = line.rstrip(b"\r")
if len(line) > self._max_field_size:
raise LineTooLong(line[:100] + b"...", self._max_field_size)
self._trailer_lines.append(line)
if len(self._trailer_lines) > self._max_trailers:
raise BadHttpMessage("Too many trailers received")
# \r\n\r\n found, end of stream
if self._trailer_lines[-1] == b"":
# Headers and trailers are defined the same way,
# so we reuse the HeadersParser here.
try:
trailers, raw_trailers = self._headers_parser.parse_headers(
self._trailer_lines
)
finally:
self._trailer_lines.clear()
self.payload.feed_eof()
return PayloadState.PAYLOAD_COMPLETE, chunk
# Read all bytes until eof
elif self._type == ParseState.PARSE_UNTIL_EOF:
self._more_data_available = self.payload.feed_data(chunk)
while self._more_data_available:View on GitHub (pinned to d041d4d0fd)