aio-libs/aiohttp · error · BadStatusLine

Bad status line

Error message

Bad status line {line!r}

What it means

Raised when the protocol version token in the request line does not match VERSRE HTTP/(\d)\.(\d). The request line must terminate with a valid 'HTTP/x.y' token (e.g. HTTP/1.0, HTTP/1.1, HTTP/2.0).

Solutions

  1. Send 'HTTP/1.0' or 'HTTP/1.1' (or 'HTTP/2.0') exactly as the version token.
  2. Use a real HTTP client library instead of raw sockets.
  3. Audit any code that builds request lines by hand.

Example fix

# before
sock.send(b'GET / HTTP1.1\r\n\r\n')

# after
sock.send(b'GET / HTTP/1.1\r\n\r\n')
Defensive patterns

Strategy: validation

Validate before calling

import re
_VERS = re.compile(r'HTTP/(\d)\.(\d)', re.ASCII)
def format_request_line(method: str, path: str) -> str:
    if not _VERS.fullmatch('HTTP/1.1'):
        raise ValueError
    return f'{method} {path} HTTP/1.1'

Type guard

import re
_VERS = re.compile(r'HTTP/(\d)\.(\d)', re.ASCII)
def is_valid_version(tok: str) -> bool:
    return bool(_VERS.fullmatch(tok))

Try / catch

from aiohttp.http_exceptions import BadStatusLine, BadHttpMessage
try:
    ...parse...
except BadStatusLine as e:
    # e.line holds the offending request line; drop the peer
    ...

Prevention

When it happens

Trigger: A version that is missing or malformed: 'GET / HTTP1.1', 'GET / 1.1', 'GET / HTTP/1', 'GET / FTP/1.0', or 'GET /' with no version.

Common situations: Hand-crafted requests via raw sockets or netcat, custom clients with wrong version formatting, fuzzing, proxies injecting junk into the request line.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/a71b654e08536454. Report an issue: GitHub.

Appendix: source

Thrown at aiohttp/http_parser.py:671

    """

    def parse_message(self, lines: list[bytes]) -> RawRequestMessage:
        # request line
        line = lines[0].decode("utf-8", "surrogateescape")
        try:
            method, path, version = line.split(" ", maxsplit=2)
        except ValueError:
            raise BadHttpMethod(line) from None

        # method
        if not TOKENRE.fullmatch(method):
            raise BadHttpMethod(method)
        method = method.upper()

        # version
        match = VERSRE.fullmatch(version)
        if match is None:
            raise BadStatusLine(line)
        version_o = HttpVersion(int(match.group(1)), int(match.group(2)))

        if method == "CONNECT":
            # authority-form,
            # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.3
            url = URL.build(authority=path, encoded=True)
        elif path.startswith("/"):
            # origin-form,
            # https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.1
            path_part, _hash_separator, url_fragment = path.partition("#")
            path_part, _question_mark_separator, qs_part = path_part.partition("?")

            # NOTE: `yarl.URL.build()` is used to mimic what the Cython-based
            # NOTE: parser does, otherwise it results into the same
            # NOTE: HTTP Request-Line input producing different
            # NOTE: `yarl.URL()` objects
            url = URL.build(
                path=path_part,

View on GitHub (pinned to d041d4d0fd)