aio-libs/aiohttp · error · ValueError
Method cannot contain non-token characters {method!r} (found
Error message
Method cannot contain non-token characters {method!r} (found at least {match!r}) What it means
Raised as ValueError in ClientRequest.__init__ when the HTTP method string contains any character not allowed in an HTTP token. The regex _CONTAINS_CONTROL_CHAR_RE matches anything outside [-!#$%&'*+.^_`|~0-9a-zA-Z], rejecting control characters, whitespace, and other separators that would corrupt the request line or enable request smuggling.
Source
Thrown at aiohttp/client_reqrep.py:827
_skip_auto_headers: "CIMultiDict[None] | None" = None
# N.B.
# Adding __del__ method with self._writer closing doesn't make sense
# because _writer is instance method, thus it keeps a reference to self.
# Until writer has finished finalizer will not be called.
def __init__(
self,
method: str,
url: URL,
*,
headers: CIMultiDict[str],
loop: asyncio.AbstractEventLoop,
ssl: SSLContext | bool | Fingerprint,
trust_env: bool = False,
):
if match := _CONTAINS_CONTROL_CHAR_RE.search(method):
raise ValueError(
f"Method cannot contain non-token characters {method!r} "
f"(found at least {match.group()!r})"
)
# URL forbids subclasses, so a simple type check is enough.
assert type(url) is URL, url
self.original_url = url
self.url = url.with_fragment(None) if url.raw_fragment else url
self.method = method.upper()
self.loop = loop
self._ssl = ssl
if loop.get_debug():
self._source_traceback = traceback.extract_stack(sys._getframe(1))
if not url.raw_host:
raise InvalidURL(url)
self._update_headers(headers)
if url.raw_user or url.raw_password:View on GitHub (pinned to c0ef574e29)
Solutions
- Sanitize method strings: strip whitespace and validate against an allowlist of known methods.
- Never build the method from untrusted raw input; map user choices to constants.
- Use upper-case standard tokens: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS.
- If a custom method is required, ensure it contains only token characters per RFC 7230.
Example fix
# before
method = user_input # could contain '\n' or spaces
await session.request(method, url)
# after
ALLOWED = {"GET","POST","PUT","PATCH","DELETE","HEAD","OPTIONS"}
method = user_input.strip().upper()
if method not in ALLOWED:
raise ValueError(f"unsupported method {method!r}")
await session.request(method, url) Defensive patterns
Strategy: validation
Validate before calling
import re
TOKEN_RE = re.compile(r"^[-!#$%&'*+.^_`|~0-9a-zA-Z]+$")
if not TOKEN_RE.fullmatch(method):
raise ValueError(f'invalid HTTP method {method!r}') Type guard
def is_valid_http_method(method: str) -> bool:
import re
return isinstance(method, str) and bool(
re.fullmatch(r"[-!#$%&'*+.^_`|~0-9a-zA-Z]+", method)
) Prevention
- Build methods only from an allowlist of constants.
- Sanitize untrusted input: strip and validate before passing.
- Never interpolate user input into the method string.
When it happens
Trigger: Fires at line 826-830 via regex search. Triggered by methods like 'GET\r\n', 'POST ' (trailing space), 'get\x00', custom methods with slashes, or methods built from untrusted input containing newlines.
Common situations: Building the method from user/config input without sanitizing; CRLF injection attempts; copy-paste introducing trailing whitespace; methods like 'PATCH/json' mistakenly used.
Related errors
- Invalid Content-Length header: {content_length_hdr!r}
- Duplicate '{name}' header found.
- Transfer-Encoding can't be present with Content-Length
- Request has duplicate `chunked` Transfer-Encoding
- Reason cannot contain \r or \n
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/fc3d4ad3f829815a.json.
Report an issue: GitHub.