{"record":{"id":"1ae8def7e9807e3c","repo":"aio-libs/aiohttp","slug":"only-async-functions-are-allowed-as-web-handlers","errorCode":null,"errorMessage":"Only async functions are allowed as web-handlers, got {handler!r}","messagePattern":"Only async functions are allowed as web-handlers, got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_urldispatcher.py","lineNumber":175,"sourceCode":"        if expect_handler is None:\n            expect_handler = _default_expect_handler\n\n        assert inspect.iscoroutinefunction(expect_handler) or (\n            sys.version_info < (3, 14) and asyncio.iscoroutinefunction(expect_handler)  # type: ignore[deprecated]\n        ), f\"Coroutine is expected, got {expect_handler!r}\"\n\n        method = method.upper()\n        if not HTTP_METHOD_RE.match(method):\n            raise ValueError(f\"{method} is not allowed HTTP method\")\n\n        if inspect.iscoroutinefunction(handler) or (\n            sys.version_info < (3, 14) and asyncio.iscoroutinefunction(handler)  # type: ignore[deprecated]\n        ):\n            pass\n        elif isinstance(handler, type) and issubclass(handler, AbstractView):\n            pass\n        else:\n            raise TypeError(\n                f\"Only async functions are allowed as web-handlers, got {handler!r}\"\n            )\n\n        self._method = method\n        self._handler = handler\n        self._expect_handler = expect_handler\n        self._resource = resource\n\n    @property\n    def method(self) -> str:\n        return self._method\n\n    @property\n    def handler(self) -> Handler:\n        return self._handler\n\n    @property\n    @abc.abstractmethod","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/web_urldispatcher.py#L157-L193","documentation":"A web handler registered on a route must be either an async function (coroutine function) or a class that subclasses AbstractView (class-based views). The check uses inspect.iscoroutinefunction and, as a fallback for Python < 3.14, asyncio.iscoroutinefunction. aiohttp will await the handler during request processing, so a plain synchronous function or a non-AbstractView class would fail with a TypeError at request time — the router catches this at registration instead.","triggerScenarios":"Calling add_route/add_get/add_post/etc. with a regular def (non-async) function; passing a class that does not inherit from AbstractView; passing a callable object instance rather than a function or view class; passing a functools.partial or wrapped object that is not itself a coroutine function.","commonSituations":"Forgetting `async def` when writing a handler; migrating a Flask-style sync handler to aiohttp without adding async; using a class-based view without subclassing web.View (which is itself AbstractView); wrapping handlers with a synchronous decorator that hides the coroutine nature.","solutions":["Declare the handler as `async def handler(request): ...`.","For class-based views, inherit from aiohttp.web.View and define async methods named after HTTP verbs (get, post, etc.).","If using a decorator, ensure it preserves the coroutine function (use functools.wraps and re-async the wrapper).","Replace any partial or lambda with a proper async def."],"exampleFix":"// before\ndef hello(request):\n    return web.Response(text='hi')\napp.router.add_get('/', hello)\n// after\nasync def hello(request):\n    return web.Response(text='hi')\napp.router.add_get('/', hello)","handlingStrategy":"type-guard","validationCode":"import inspect\nfrom aiohttp.web import View\nfrom aiohttp.abc import AbstractView\n\ndef assert_handler(h):\n    if inspect.iscoroutinefunction(h):\n        return h\n    if isinstance(h, type) and issubclass(h, AbstractView):\n        return h\n    raise TypeError(f'Handler must be async function or AbstractView subclass: {h!r}')","typeGuard":"import inspect\nfrom aiohttp.abc import AbstractView\n\ndef is_valid_handler(h) -> bool:\n    return inspect.iscoroutinefunction(h) or (isinstance(h, type) and issubclass(h, AbstractView))","tryCatchPattern":null,"preventionTips":["Always write handlers with `async def`.","For class-based views, inherit from aiohttp.web.View.","When using decorators, wrap with async def and functools.wraps to preserve the coroutine nature.","Lint with a rule that flags sync functions passed to add_route."],"tags":["routing","handler","async","type-check"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}