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
- For HTTPS-on-HTTP: enable TLS on the aiohttp server or use the correct port/scheme.
- For malformed: inspect the raw bytes the peer sent.
- 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
- Separate TLS and plaintext listeners on different ports.
- When using raw sockets, sanity-check the first bytes before assuming HTTP.
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
- Bad HTTP method in status line
- Bad status line
- Duplicate ' ' header found.
- Transfer-Encoding can't be present with Content-Length
- Bad line ending, expected CRLF
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)