aio-libs/aiohttp · error · BadHttpMessage
Transfer-Encoding can't be present with Content-Length
Error message
Transfer-Encoding can't be present with Content-Length
What it means
Raised when both Transfer-Encoding and Content-Length are present in the same message. RFC 9110 section 6.6.3 forbids the combination because it enables request smuggling (front/back parsers disagree on body framing). aiohttp rejects it outright rather than trying to disambiguate.
Solutions
- Send exactly one of Transfer-Encoding or Content-Length.
- When sending chunked, do not set Content-Length.
- Audit any proxy / header-rewriting middleware that may add the other header.
Example fix
# before headers['Transfer-Encoding'] = 'chunked' headers['Content-Length'] = '100' # conflict # after - choose one headers['Transfer-Encoding'] = 'chunked' # del headers['Content-Length']
Defensive patterns
Strategy: validation
Validate before calling
def framing_is_unambiguous(headers) -> bool:
names = {k.lower() for k in headers}
return not ({'transfer-encoding', 'content-length'} <= names) Type guard
def only_one_framing_header(headers) -> bool:
names = {k.lower() for k in headers}
return not ({'transfer-encoding', 'content-length'} <= names) Try / catch
from aiohttp.http_exceptions import BadHttpMessage
try:
...parse...
except BadHttpMessage as e:
if 'Transfer-Encoding' in str(e):
transport.close() # likely smuggling; do not retry Prevention
- Never emit both TE and CL on the same message.
- When forwarding, strip one framing header before adding the other.
When it happens
Trigger: A peer sends a request or response carrying both Transfer-Encoding (e.g. chunked) and Content-Length.
Common situations: Proxies forwarding a client's TE and adding CL (or vice versa), caches that mangle headers, deliberate smuggling attacks, buggy custom servers combining both.
Related errors
- Duplicate ' ' header found.
- Bad HTTP method in status line
- Invalid HTTP header
- Bad HTTP method in status line
- Bad line ending, expected CRLF
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/949d8f3a00373c20.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/http_parser.py:633
close_conn = False
# https://www.rfc-editor.org/rfc/rfc9110.html#name-101-switching-protocols
if "upgrade" in conn_tokens and headers.get(hdrs.UPGRADE):
upgrade = True
# encoding
enc = headers.get(hdrs.CONTENT_ENCODING, "")
if enc.isascii() and enc.lower() in {"gzip", "deflate", "br", "zstd"}:
encoding = enc
# chunking
te = headers.get(hdrs.TRANSFER_ENCODING)
if te is not None:
if self._is_chunked_te(te):
chunked = True
if hdrs.CONTENT_LENGTH in headers:
raise BadHttpMessage(
"Transfer-Encoding can't be present with Content-Length",
)
return (headers, raw_headers, close_conn, encoding, upgrade, chunked)
def set_upgraded(self, val: bool) -> None:
"""Set connection upgraded (to websocket) mode.
:param bool val: new state.
"""
self._upgraded = val
class HttpRequestParser(HttpParser[RawRequestMessage]):
"""Read request status line.
Exception .http_exceptions.BadStatusLine
could be raised in case of any errors in status line.View on GitHub (pinned to d041d4d0fd)