aio-libs/aiohttp · error · BadHttpMethod
Bad HTTP method in status line
Error message
Bad HTTP method in status line {method!r} What it means
Raised when the method token does not fully match TOKENRE [0-9A-Za-z!#$%&'*+-.^_`|~]+. Methods must be RFC 9110 tokens: no spaces, no control bytes, no delimiters.
Solutions
- Send a standard method token (GET, POST, ...).
- Validate custom methods against the token charset before sending.
- Reject malformed methods at the edge.
Example fix
# before sock.send(b'G@T / HTTP/1.1\r\n') # after sock.send(b'GET / HTTP/1.1\r\n')
Defensive patterns
Strategy: validation
Validate before calling
import re
_TOKEN = re.compile(r"[0-9A-Za-z!#$%&'*+-.^_`|~]+")
def safe_method(m: str) -> str | None:
return m if _TOKEN.fullmatch(m) else None Type guard
import re
_TOKEN = re.compile(r"[0-9A-Za-z!#$%&'*+-.^_`|~]+")
def is_method_token(m: str) -> bool:
return bool(_TOKEN.fullmatch(m)) Try / catch
from aiohttp.http_exceptions import BadHttpMethod
try:
...parse...
except BadHttpMethod as e:
transport.close() Prevention
- Treat the method as a token; never allow user-controlled delimiters in it.
- When accepting custom methods, validate them against TOKENRE first.
When it happens
Trigger: A method containing any non-token character: 'GE/T', 'GET\r' (CRLF injection), an empty method, or a custom method with delimiters.
Common situations: Custom clients sending malformed methods, CRLF-injection attacks, fuzzing, encoding bugs.
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/49c689896da5eeb8.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/http_parser.py:665
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,
# 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("?")
View on GitHub (pinned to d041d4d0fd)