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

  1. Sanitize method strings: strip whitespace and validate against an allowlist of known methods.
  2. Never build the method from untrusted raw input; map user choices to constants.
  3. Use upper-case standard tokens: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS.
  4. 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

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


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/fc3d4ad3f829815a.json. Report an issue: GitHub.