{"id":"cf31f2735c6a95d7","repo":"aio-libs/aiohttp","slug":"request-has-invalid-transfer-encoding","errorCode":null,"errorMessage":"Request has invalid `Transfer-Encoding`","messagePattern":"Request has invalid `Transfer-Encoding`","errorType":"http","errorClass":"BadHttpMessage","httpStatus":400,"severity":"error","filePath":"aiohttp/http_parser.py","lineNumber":751,"sourceCode":"            upgrade,\n            chunked,\n            url,\n        )\n\n    def _is_chunked_te(self, te: str) -> bool:\n        # https://www.rfc-editor.org/rfc/rfc9112#section-7.1-3\n        # \"A sender MUST NOT apply the chunked transfer coding more\n        #  than once to a message body\"\n        parts = [p.strip(\" \\t\") for p in te.split(\",\")]\n        chunked_count = sum(1 for p in parts if p.isascii() and p.lower() == \"chunked\")\n        if chunked_count > 1:\n            raise BadHttpMessage(\"Request has duplicate `chunked` Transfer-Encoding\")\n        last = parts[-1]\n        # .lower() transforms some non-ascii chars, so must check first.\n        if last.isascii() and last.lower() == \"chunked\":\n            return True\n        # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.3\n        raise BadHttpMessage(\"Request has invalid `Transfer-Encoding`\")\n\n\nclass HttpResponseParser(HttpParser[RawResponseMessage]):\n    \"\"\"Read response status line and headers.\n\n    BadStatusLine could be raised in case of any errors in status line.\n    Returns RawResponseMessage.\n    \"\"\"\n\n    protocol: \"ResponseHandler\"\n\n    # Lax mode should only be enabled on response parser.\n    lax = not DEBUG\n\n    def feed_data(\n        self,\n        data: bytes,\n        SEP: _SEP | None = None,","sourceCodeStart":733,"sourceCodeEnd":769,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/http_parser.py#L733-L769","documentation":"Raised by the request parser in HttpRequestParser._is_chunked_te when an inbound request carries a Transfer-Encoding header whose final token is not 'chunked'. aiohttp only supports chunked transfer-coding for request bodies; any other coding (or a TE that does not terminate in chunked) is rejected per RFC 9112 section 6.3.","triggerScenarios":"A client sends a request with Transfer-Encoding set to a non-chunked value (e.g. `Transfer-Encoding: gzip`, `compress`, or `identity`) or a list that does not end in `chunked`. The request parser calls _is_chunked_te and, because the last comma-separated token is not 'chunked', raises BadHttpMessage (HTTP 400).","commonSituations":"Misconfigured reverse proxies or custom HTTP clients injecting a raw Transfer-Encoding; curl/scripting that sets TE manually; interop with servers that send non-standard codings; testing against a server that forwards a hop-by-hop TE.","solutions":["Inspect the request's Transfer-Encoding header and ensure it ends with 'chunked' or remove it and use Content-Length instead.","If you need a content encoding, use Content-Encoding (end-to-end) rather than Transfer-Encoding (hop-by-hop).","On the server side, catch BadHttpMessage to return a clean 400 instead of crashing the handler."],"exampleFix":"// before: client sends\n//   Transfer-Encoding: gzip\r\n\n# after: use Content-Length or chunked\n#   Content-Length: 42\r\n  (or)\n#   Transfer-Encoding: chunked\r\n","handlingStrategy":"try-catch","validationCode":"te = request.headers.get('Transfer-Encoding', '')\nparts = [p.strip().lower() for p in te.split(',')]\nif parts and parts[-1] != 'chunked' and te:\n    # reject before the parser does\n    raise ValueError('unsupported Transfer-Encoding')","typeGuard":"def is_valid_request_te(te: str) -> bool:\n    if not te:\n        return True\n    parts = [p.strip().lower() for p in te.split(',')]\n    return parts.count('chunked') <= 1 and parts[-1] == 'chunked'","tryCatchPattern":"from aiohttp.http_exceptions import BadHttpMessage\ntry:\n    await request.read()\nexcept BadHttpMessage:\n    return web.Response(status=400, text='Bad Transfer-Encoding')","preventionTips":["Never set Transfer-Encoding manually from a client; let the library choose chunked vs Content-Length.","Remember TE is hop-by-hop and stripped by proxies — debug with a raw TCP capture."],"tags":["http","parser","request","transfer-encoding","rfc9112"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}