{"id":"1fa9ec9c82669274","repo":"aio-libs/aiohttp","slug":"invalid-http-header-hdr-r","errorCode":null,"errorMessage":"Invalid HTTP header: {hdr!r}","messagePattern":"Invalid HTTP header: (.+?)","errorType":"http","errorClass":"InvalidHeader","httpStatus":400,"severity":"error","filePath":"aiohttp/http_parser.py","lineNumber":181,"sourceCode":"    def __init__(self, max_field_size: int = 8190, lax: bool = False) -> None:\n        self.max_field_size = max_field_size\n        self._lax = lax\n\n    def parse_headers(self, lines: list[bytes]) -> tuple[HeadersDictProxy, RawHeaders]:\n        headers: CIMultiDict[str] = CIMultiDict()\n        # note: \"raw\" does not mean inclusion of OWS before/after the field value\n        raw_headers = []\n\n        lines_idx = 0\n        line = lines[lines_idx]\n        line_count = len(lines)\n\n        while line:\n            # Parse initial header name : value pair.\n            try:\n                bname, bvalue = line.split(b\":\", 1)\n            except ValueError:\n                raise InvalidHeader(line) from None\n\n            if len(bname) == 0:\n                raise InvalidHeader(bname)\n\n            # https://www.rfc-editor.org/rfc/rfc9112.html#section-5.1-2\n            if {bname[0], bname[-1]} & {32, 9}:  # {\" \", \"\\t\"}\n                raise InvalidHeader(line)\n\n            bvalue = bvalue.lstrip(b\" \\t\")\n            name = bname.decode(\"utf-8\", \"surrogateescape\")\n            if not TOKENRE.fullmatch(name):\n                raise InvalidHeader(bname)\n\n            # next line\n            lines_idx += 1\n            line = lines[lines_idx]\n\n            # consume continuation lines","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/http_parser.py#L163-L199","documentation":"Raised by HeadersParser.parse_headers as InvalidHeader (a BadHttpMessage subclass, HTTP 400) when a header line cannot be split into name:value on a colon — i.e. line.split(b':', 1) returns a single element, meaning the line has no colon at all. This is a malformed request/response header line.","triggerScenarios":"A peer (server or client) sending a header line like b'Accept-Encoding gzip' (space instead of colon), or a bare token line with no colon. The parser is fed raw header lines split on CRLF.","commonSituations":"Malformed upstream responses; hand-rolled HTTP clients/servers emitting bad headers; proxies rewriting headers incorrectly; binary garbage interpreted as headers.","solutions":["If you control the peer, fix it to emit 'Name: Value' lines.","Increase robustness on the client side by catching aiohttp.ClientResponseError / BadHttpMessage.","Inspect the raw response with a lower-level tool to find the offending header."],"exampleFix":"// before\n# peer sends: Accept-Encoding gzip   (no colon)\n// after\n# peer sends: Accept-Encoding: gzip","handlingStrategy":"try-catch","validationCode":"def header_has_colon(line: bytes) -> bool:\n    return b':' in line","typeGuard":null,"tryCatchPattern":"from aiohttp.http_exceptions import InvalidHeader\ntry:\n    headers, raw = parser.parse_headers(lines)\nexcept InvalidHeader as e:\n    log.warning('peer sent malformed header: %r', e.hdr)","preventionTips":["If you produce headers, always emit 'Name: Value' with a colon.","On the client side, catch ClientResponseError/InvalidHeader for malformed servers.","Inspect raw bytes with curl -i to locate the offending header."],"tags":["http-parser","headers","bad-http-message","malformed"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}