aio-libs/aiohttp · error · RuntimeError

Added route will never be executed, method {route.method} is

Error message

Added route will never be executed, method {route.method} is already registered

What it means

Raised as RuntimeError by Resource.add_route when a route for the same HTTP method (or ANY) is already registered on that resource. Because route resolution returns the first registered match, a duplicate would be silently unreachable, so aiohttp rejects it to surface the likely configuration mistake.

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 c0ef574e29)

Solutions

  1. Remove the duplicate registration — keep a single handler per method per resource.
  2. If you need different handlers, give them distinct paths or distinct methods.
  3. Guard against double-import by registering routes in a function called once, or use an idempotent setup.
  4. If you intended to override, first remove/replace the existing route rather than adding alongside.

Example fix

# before
resource.add_route('GET', h1)
resource.add_route('GET', h2)  # raises

# after
resource.add_route('GET', h1)
resource.add_route('POST', h2)
Defensive patterns

Strategy: validation

Validate before calling

def register_unique(resource, method, handler):
    if method in resource._routes or (resource._any_route and method == '*'):
        raise RuntimeError(f"method {method} already registered on {resource}")
    return resource.add_route(method, handler)

Try / catch

try:
    resource.add_route(method, handler)
except RuntimeError as e:
    if 'already registered' in str(e):
        log.warning("skipping duplicate route for %s", method)
    else:
        raise

Prevention

When it happens

Trigger: Calling resource.add_route('GET', handler1) then resource.add_route('GET', handler2) on the same Resource. Also when add_get is called twice on the same path/resource, or mixing add_route('GET') with an ANY route already present (the ANY route shadows it).

Common situations: Two blueprint/blueprint registrations applying to the same prefix; importing a routes module twice; copy-paste creating duplicate add_get calls; registering both a method-specific route and a '*' (ANY) route on the same resource where ANY was registered first.

Related errors


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