aio-libs/aiohttp · error · InvalidHeader

Invalid HTTP header: {hdr!r}

Error message

Invalid HTTP header: {hdr!r}

What it means

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.

Source

Thrown at aiohttp/http_parser.py:181

    def __init__(self, max_field_size: int = 8190, lax: bool = False) -> None:
        self.max_field_size = max_field_size
        self._lax = lax

    def parse_headers(self, lines: list[bytes]) -> tuple[HeadersDictProxy, RawHeaders]:
        headers: CIMultiDict[str] = CIMultiDict()
        # note: "raw" does not mean inclusion of OWS before/after the field value
        raw_headers = []

        lines_idx = 0
        line = lines[lines_idx]
        line_count = len(lines)

        while line:
            # Parse initial header name : value pair.
            try:
                bname, bvalue = line.split(b":", 1)
            except ValueError:
                raise InvalidHeader(line) from None

            if len(bname) == 0:
                raise InvalidHeader(bname)

            # https://www.rfc-editor.org/rfc/rfc9112.html#section-5.1-2
            if {bname[0], bname[-1]} & {32, 9}:  # {" ", "\t"}
                raise InvalidHeader(line)

            bvalue = bvalue.lstrip(b" \t")
            name = bname.decode("utf-8", "surrogateescape")
            if not TOKENRE.fullmatch(name):
                raise InvalidHeader(bname)

            # next line
            lines_idx += 1
            line = lines[lines_idx]

            # consume continuation lines

View on GitHub (pinned to c0ef574e29)

Solutions

  1. If you control the peer, fix it to emit 'Name: Value' lines.
  2. Increase robustness on the client side by catching aiohttp.ClientResponseError / BadHttpMessage.
  3. Inspect the raw response with a lower-level tool to find the offending header.

Example fix

// before
# peer sends: Accept-Encoding gzip   (no colon)
// after
# peer sends: Accept-Encoding: gzip
Defensive patterns

Strategy: try-catch

Validate before calling

def header_has_colon(line: bytes) -> bool:
    return b':' in line

Try / catch

from aiohttp.http_exceptions import InvalidHeader
try:
    headers, raw = parser.parse_headers(lines)
except InvalidHeader as e:
    log.warning('peer sent malformed header: %r', e.hdr)

Prevention

When it happens

Trigger: 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.

Common situations: Malformed upstream responses; hand-rolled HTTP clients/servers emitting bad headers; proxies rewriting headers incorrectly; binary garbage interpreted as headers.

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/1fa9ec9c82669274.json. Report an issue: GitHub.