aio-libs/aiohttp · error · BadHttpMethod

Bad HTTP method in status line

Error message

Bad HTTP method in status line {line!r}

What it means

Raised when the request line cannot be split into three whitespace-separated tokens (method, path, version) - line.split(' ', maxsplit=2) raises ValueError. Special case: if the line starts with bytes \x16\x03 (a TLS record header), the message becomes 'Received HTTPS traffic on an HTTP port'.

Solutions

  1. For HTTPS-on-HTTP: enable TLS on the aiohttp server or use the correct port/scheme.
  2. For malformed: inspect the raw bytes the peer sent.
  3. Add a protocol-detecting proxy that routes TLS to the TLS port.

Example fix

# before - client uses https:// against port 80
# after - enable TLS on the server, or use http:// scheme
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_ctx.load_cert_chain('cert.pem', 'key.pem')
await web.TCPSite(runner, port=443, ssl_context=ssl_ctx).start()
Defensive patterns

Strategy: try-catch

Validate before calling

# cannot 'validate' remote bytes; detect TLS-on-HTTP at the edge instead
def looks_like_tls(first_bytes: bytes) -> bool:
    return first_bytes[:2] == b'\x16\x03'

Type guard

def looks_like_tls(first_bytes: bytes) -> bool:
    return first_bytes[:2] == b'\x16\x03'

Try / catch

from aiohttp.http_exceptions import BadHttpMethod, BadHttpMessage
try:
    ...parse...
except BadHttpMethod as e:
    if 'HTTPS traffic on an HTTP port' in str(e):
        # terminate or redirect to the TLS port
        ...

Prevention

When it happens

Trigger: A request line with fewer than two spaces ('GET\r\n', 'GARBAGE', an empty line), or a TLS ClientHello (\x16\x03...) hitting a plaintext HTTP port.

Common situations: HTTPS client connecting to a plaintext HTTP port (the \x16\x03 case), corrupted requests, raw non-HTTP TCP traffic hitting the HTTP port, fuzzing.

Related errors


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

Appendix: source

Thrown at aiohttp/http_parser.py:661

        """
        self._upgraded = val


class HttpRequestParser(HttpParser[RawRequestMessage]):
    """Read request status line.

    Exception .http_exceptions.BadStatusLine
    could be raised in case of any errors in status line.
    Returns RawRequestMessage.
    """

    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,

View on GitHub (pinned to d041d4d0fd)