aio-libs/aiohttp · error · RuntimeError
Added route will never be executed, method
Error message
Added route will never be executed, method {route.method} is already registered What it means
Resource.add_route refuses to register a second route for the same HTTP method (or for the catch-all ANY method) on the same Resource, because the second handler would be unreachable — the first registered route always wins. The check looks up self._routes.get(method) and also checks self._any_route. Raising early prevents silent shadowing where a developer believes two handlers both run but only one ever does.
Solutions
- Remove or rename the duplicate registration so each (resource, method) pair has exactly one handler.
- If you need different handlers, give the paths distinct templates or use distinct resource names.
- Search the route table for the conflicting method/path before re-registering.
Example fix
// before
resource.add_route('GET', get_items)
resource.add_route('GET', get_items_v2) # RuntimeError
// after
resource.add_route('GET', get_items_v2) # only register the one you want Defensive patterns
Strategy: validation
Validate before calling
def safe_add_route(resource, method, handler):
if method in getattr(resource, '_routes', {}):
raise RuntimeError(f'{method} already registered on this resource')
return resource.add_route(method, handler) Prevention
- Centralize route registration in one module to avoid duplicates.
- Use named resources and a registry so collisions are visible.
- Add a startup self-test that asserts no duplicate (path, method) pairs.
When it happens
Trigger: Calling resource.add_route('GET', h1) then resource.add_route('GET', h2) on the same Resource; or registering ANY handler (via add_route with method '*') and then a specific method. Also happens via app.router.add_get twice on the same path when they resolve to the same underlying Resource.
Common situations: Copy-pasting route registration and forgetting the first one exists; refactoring that moves routes without removing the originals; conditional registration that runs twice; building routes in a loop where a path collides.
Related errors
- Bad pattern
- Cannot change apps stack after .freeze() call
- Domain cannot be empty
- Domain must be str
- Domain must be str
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/489014cb915a8f4c.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/web_urldispatcher.py:323
raise HTTPExpectationFailed(text="Unknown Expect: %s" % expect)
class Resource(AbstractResource):
def __init__(self, *, name: str | None = None) -> None:
super().__init__(name=name)
self._routes: dict[str, ResourceRoute] = {}
self._any_route: ResourceRoute | None = None
self._allowed_methods: set[str] = set()
def add_route(
self,
method: str,
handler: type[AbstractView] | Handler,
*,
expect_handler: _ExpectHandler | None = None,
) -> "ResourceRoute":
if route := self._routes.get(method, self._any_route):
raise RuntimeError(
"Added route will never be executed, "
f"method {route.method} is already "
"registered"
)
route_obj = ResourceRoute(method, handler, self, expect_handler=expect_handler)
self.register_route(route_obj)
return route_obj
def register_route(self, route: "ResourceRoute") -> None:
assert isinstance(
route, ResourceRoute
), f"Instance of Route class is required, got {route!r}"
if route.method == hdrs.METH_ANY:
self._any_route = route
self._allowed_methods.add(route.method)
self._routes[route.method] = route
View on GitHub (pinned to d041d4d0fd)