aio-libs/aiohttp · error · ValueError
{method} is not allowed HTTP method
Error message
{method} is not allowed HTTP method What it means
Raised as ValueError by Route.__init__ (via add_route/add_get/add_post etc.) when the HTTP method string does not match HTTP_METHOD_RE (token chars defined by RFC 7230). The method is uppercased then matched against the allowed token grammar, so malformed methods, methods with spaces, or typos are rejected before route registration.
Source
Thrown at aiohttp/web_urldispatcher.py:166
class AbstractRoute(abc.ABC):
def __init__(
self,
method: str,
handler: Handler | type[AbstractView],
*,
expect_handler: _ExpectHandler | None = None,
resource: AbstractResource | None = None,
) -> None:
if expect_handler is None:
expect_handler = _default_expect_handler
assert inspect.iscoroutinefunction(expect_handler) or (
sys.version_info < (3, 14) and asyncio.iscoroutinefunction(expect_handler) # type: ignore[deprecated]
), f"Coroutine is expected, got {expect_handler!r}"
method = method.upper()
if not HTTP_METHOD_RE.match(method):
raise ValueError(f"{method} is not allowed HTTP method")
if inspect.iscoroutinefunction(handler) or (
sys.version_info < (3, 14) and asyncio.iscoroutinefunction(handler) # type: ignore[deprecated]
):
pass
elif isinstance(handler, type) and issubclass(handler, AbstractView):
pass
else:
raise TypeError(
f"Only async functions are allowed as web-handlers, got {handler!r}"
)
self._method = method
self._handler = handler
self._expect_handler = expect_handler
self._resource = resource
@propertyView on GitHub (pinned to c0ef574e29)
Solutions
- Use a single valid RFC 7230 method token: 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'.
- For matching any method use app.router.add_route('*', path, handler) which maps to METH_ANY.
- Strip/validate methods read from external config: method.strip().upper() before passing.
- Register separate routes for each method instead of comma/space-separated lists.
Example fix
# before
app.router.add_route('GET POST', '/items', handler)
# after
app.router.add_route('GET', '/items', handler)
app.router.add_route('POST', '/items', handler) Defensive patterns
Strategy: validation
Validate before calling
import re
from aiohttp.web_urldispatcher import HTTP_METHOD_RE
def normalize_method(method: str) -> str:
m = method.strip().upper()
if not HTTP_METHOD_RE.match(m):
raise ValueError(f"Invalid HTTP method: {method!r}")
return m Type guard
from aiohttp.web_urldispatcher import HTTP_METHOD_RE
def is_valid_method(method: str) -> bool:
return isinstance(method, str) and bool(HTTP_METHOD_RE.match(method.strip().upper())) Try / catch
try:
app.router.add_route(method, path, handler)
except ValueError as e:
raise ValueError(f"Rejected method {method!r}: {e}") from e Prevention
- Centralize method normalization for config-driven routes.
- Use the aiohttp hdrs constants (hdrs.METH_GET) for stock methods.
- Lint route registrations in a test that exercises every add_route call.
When it happens
Trigger: Calling app.router.add_route('GETT', '/', handler), add_route('GET POST', ...), add_route('', ...), or passing a method with illegal characters. Also app.router.add_get with a typo like 'get ' (trailing space) after uppercasing yields 'GET ' which fails the regex.
Common situations: Typing the method name; passing a method read from config with whitespace/newline; concatenating methods; using lowercase is fine (uppercased internally) but embedded spaces or slashes are not; confusion with hdrs.METH_ANY which is handled separately for ANY routes.
Related errors
- Invalid path '{path}'['{part}']
- Added route will never be executed, method {route.method} is
- Bad pattern '{pattern}': {exc}
- Only async functions are allowed as web-handlers, got {handl
- Cannot change apps stack after .freeze() call
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/753dbb15c044aed5.json.
Report an issue: GitHub.