{"id":"fc3d4ad3f829815a","repo":"aio-libs/aiohttp","slug":"method-cannot-contain-non-token-characters-method","errorCode":null,"errorMessage":"Method cannot contain non-token characters {method!r} (found at least {match!r})","messagePattern":"Method cannot contain non-token characters (.+?) \\(found at least (.+?)\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/client_reqrep.py","lineNumber":827,"sourceCode":"    _skip_auto_headers: \"CIMultiDict[None] | None\" = None\n\n    # N.B.\n    # Adding __del__ method with self._writer closing doesn't make sense\n    # because _writer is instance method, thus it keeps a reference to self.\n    # Until writer has finished finalizer will not be called.\n\n    def __init__(\n        self,\n        method: str,\n        url: URL,\n        *,\n        headers: CIMultiDict[str],\n        loop: asyncio.AbstractEventLoop,\n        ssl: SSLContext | bool | Fingerprint,\n        trust_env: bool = False,\n    ):\n        if match := _CONTAINS_CONTROL_CHAR_RE.search(method):\n            raise ValueError(\n                f\"Method cannot contain non-token characters {method!r} \"\n                f\"(found at least {match.group()!r})\"\n            )\n        # URL forbids subclasses, so a simple type check is enough.\n        assert type(url) is URL, url\n        self.original_url = url\n        self.url = url.with_fragment(None) if url.raw_fragment else url\n        self.method = method.upper()\n        self.loop = loop\n        self._ssl = ssl\n\n        if loop.get_debug():\n            self._source_traceback = traceback.extract_stack(sys._getframe(1))\n\n        if not url.raw_host:\n            raise InvalidURL(url)\n        self._update_headers(headers)\n        if url.raw_user or url.raw_password:","sourceCodeStart":809,"sourceCodeEnd":845,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/client_reqrep.py#L809-L845","documentation":"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.","triggerScenarios":"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.","commonSituations":"Building the method from user/config input without sanitizing; CRLF injection attempts; copy-paste introducing trailing whitespace; methods like 'PATCH/json' mistakenly used.","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."],"exampleFix":"# before\nmethod = user_input  # could contain '\\n' or spaces\nawait session.request(method, url)\n# after\nALLOWED = {\"GET\",\"POST\",\"PUT\",\"PATCH\",\"DELETE\",\"HEAD\",\"OPTIONS\"}\nmethod = user_input.strip().upper()\nif method not in ALLOWED:\n    raise ValueError(f\"unsupported method {method!r}\")\nawait session.request(method, url)","handlingStrategy":"validation","validationCode":"import re\nTOKEN_RE = re.compile(r\"^[-!#$%&'*+.^_`|~0-9a-zA-Z]+$\")\nif not TOKEN_RE.fullmatch(method):\n    raise ValueError(f'invalid HTTP method {method!r}')","typeGuard":"def is_valid_http_method(method: str) -> bool:\n    import re\n    return isinstance(method, str) and bool(\n        re.fullmatch(r\"[-!#$%&'*+.^_`|~0-9a-zA-Z]+\", method)\n    )","tryCatchPattern":null,"preventionTips":["Build methods only from an allowlist of constants.","Sanitize untrusted input: strip and validate before passing.","Never interpolate user input into the method string."],"tags":["security","http","request-smuggling","validation","injection"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}