aio-libs/aiohttp · error · BadHttpMessage
Request has duplicate `chunked` Transfer-Encoding
Error message
Request has duplicate `chunked` Transfer-Encoding
What it means
Raised by HttpRequestParser._is_chunked_te (aiohttp/http_parser.py:745) when the Transfer-Encoding value, split on commas, contains 'chunked' more than once. RFC 9112 section 7.1 forbids applying the chunked coding more than once; doing so is a request-smuggling vector against parsers that deduplicate inconsistently. Request-parser specific (the response parser uses a looser check that does not count duplicates).
Source
Thrown at aiohttp/http_parser.py:745
path,
version_o,
headers,
raw_headers,
close,
compression,
upgrade,
chunked,
url,
)
def _is_chunked_te(self, te: str) -> bool:
# https://www.rfc-editor.org/rfc/rfc9112#section-7.1-3
# "A sender MUST NOT apply the chunked transfer coding more
# than once to a message body"
parts = [p.strip(" \t") for p in te.split(",")]
chunked_count = sum(1 for p in parts if p.isascii() and p.lower() == "chunked")
if chunked_count > 1:
raise BadHttpMessage("Request has duplicate `chunked` Transfer-Encoding")
last = parts[-1]
# .lower() transforms some non-ascii chars, so must check first.
if last.isascii() and last.lower() == "chunked":
return True
# https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.3
raise BadHttpMessage("Request has invalid `Transfer-Encoding`")
class HttpResponseParser(HttpParser[RawResponseMessage]):
"""Read response status line and headers.
BadStatusLine could be raised in case of any errors in status line.
Returns RawResponseMessage.
"""
protocol: "ResponseHandler"
# Lax mode should only be enabled on response parser.View on GitHub (pinned to c0ef574e29)
Solutions
- Send 'Transfer-Encoding: chunked' exactly once, and only as the LAST coding in the list.
- Fix any proxy that appends 'chunked' redundantly; deduplicate before forwarding.
- Let aiohttp handle chunking automatically when you stream a body; do not set Transfer-Encoding manually.
Example fix
# before - chunked listed twice headers['Transfer-Encoding'] = 'chunked, chunked' # after - single chunked as the last coding headers['Transfer-Encoding'] = 'chunked' # or just stream the body and let aiohttp add TE: chunked
Defensive patterns
Strategy: validation
Validate before calling
def chunked_appears_once(te_value: str) -> bool:
parts = [p.strip().lower() for p in te_value.split(',')]
return parts.count('chunked') <= 1
te = headers.get('Transfer-Encoding')
if te is not None and not chunked_appears_once(te):
raise ValueError('duplicate chunked in Transfer-Encoding') Prevention
- Send 'chunked' at most once and only as the final TE coding
- Let the library manage chunked encoding when streaming a body
- Strip duplicate chunked at every proxy hop
When it happens
Trigger: A request with 'Transfer-Encoding: chunked, chunked' or any TE value where two or more comma-separated codings equal 'chunked'. Even 'gzip, chunked, chunked' triggers it.
Common situations: Request-smuggling attacks, proxies that append 'chunked' without deduplicating, or buggy encoders that stack the coding. Distinct from error 133 (TE+CL conflict) - here only TE is present but chunked is duplicated.
Related errors
- Duplicate '{name}' header found.
- Transfer-Encoding can't be present with Content-Length
- Request has invalid `Transfer-Encoding`
- Method cannot contain non-token characters {method!r} (found
- Bad line ending, expected CRLF
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/4d72e200bc582d7f.json.
Report an issue: GitHub.