{"record":{"id":"949d8f3a00373c20","repo":"aio-libs/aiohttp","slug":"transfer-encoding-can-t-be-present-with-content-le","errorCode":null,"errorMessage":"Transfer-Encoding can't be present with Content-Length","messagePattern":"Transfer-Encoding can't be present with Content-Length","errorType":"exception","errorClass":"BadHttpMessage","httpStatus":400,"severity":"error","filePath":"aiohttp/http_parser.py","lineNumber":633,"sourceCode":"                close_conn = False\n\n            # https://www.rfc-editor.org/rfc/rfc9110.html#name-101-switching-protocols\n            if \"upgrade\" in conn_tokens and headers.get(hdrs.UPGRADE):\n                upgrade = True\n\n        # encoding\n        enc = headers.get(hdrs.CONTENT_ENCODING, \"\")\n        if enc.isascii() and enc.lower() in {\"gzip\", \"deflate\", \"br\", \"zstd\"}:\n            encoding = enc\n\n        # chunking\n        te = headers.get(hdrs.TRANSFER_ENCODING)\n        if te is not None:\n            if self._is_chunked_te(te):\n                chunked = True\n\n            if hdrs.CONTENT_LENGTH in headers:\n                raise BadHttpMessage(\n                    \"Transfer-Encoding can't be present with Content-Length\",\n                )\n\n        return (headers, raw_headers, close_conn, encoding, upgrade, chunked)\n\n    def set_upgraded(self, val: bool) -> None:\n        \"\"\"Set connection upgraded (to websocket) mode.\n\n        :param bool val: new state.\n        \"\"\"\n        self._upgraded = val\n\n\nclass HttpRequestParser(HttpParser[RawRequestMessage]):\n    \"\"\"Read request status line.\n\n    Exception .http_exceptions.BadStatusLine\n    could be raised in case of any errors in status line.","sourceCodeStart":615,"sourceCodeEnd":651,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/http_parser.py#L615-L651","documentation":"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.","triggerScenarios":"A peer sends a request or response carrying both Transfer-Encoding (e.g. chunked) and Content-Length.","commonSituations":"Proxies forwarding a client's TE and adding CL (or vice versa), caches that mangle headers, deliberate smuggling attacks, buggy custom servers combining both.","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."],"exampleFix":"# before\nheaders['Transfer-Encoding'] = 'chunked'\nheaders['Content-Length'] = '100'   # conflict\n\n# after - choose one\nheaders['Transfer-Encoding'] = 'chunked'  # del headers['Content-Length']","handlingStrategy":"validation","validationCode":"def framing_is_unambiguous(headers) -> bool:\n    names = {k.lower() for k in headers}\n    return not ({'transfer-encoding', 'content-length'} <= names)","typeGuard":"def only_one_framing_header(headers) -> bool:\n    names = {k.lower() for k in headers}\n    return not ({'transfer-encoding', 'content-length'} <= names)","tryCatchPattern":"from aiohttp.http_exceptions import BadHttpMessage\ntry:\n    ...parse...\nexcept BadHttpMessage as e:\n    if 'Transfer-Encoding' in str(e):\n        transport.close()  # likely smuggling; do not retry","preventionTips":["Never emit both TE and CL on the same message.","When forwarding, strip one framing header before adding the other."],"tags":["http","parser","security","smuggling","header","validation"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}