aio-libs/aiohttp · error · RuntimeError

.url_for() is not supported by sub-application root

Error message

.url_for() is not supported by sub-application root

What it means

Raised as RuntimeError by PrefixedSubAppResource.url_for. Sub-application root resources are mounted under a prefix and do not have a single canonical URL to reverse, so calling url_for() on one is meaningless and explicitly rejected. Reverse URL lookups must target concrete named routes inside the sub-app (or the parent), not the sub-app mount point itself.

Source

Thrown at aiohttp/web_urldispatcher.py:729

        super().__init__(prefix)
        self._app = app
        self._add_prefix_to_resources(prefix)

    def add_prefix(self, prefix: str) -> None:
        super().add_prefix(prefix)
        self._add_prefix_to_resources(prefix)

    def _add_prefix_to_resources(self, prefix: str) -> None:
        router = self._app.router
        for resource in router.resources():
            # Since the canonical path of a resource is about
            # to change, we need to unindex it and then reindex
            router.unindex_resource(resource)
            resource.add_prefix(prefix)
            router.index_resource(resource)

    def url_for(self, *args: str, **kwargs: str) -> URL:
        raise RuntimeError(".url_for() is not supported by sub-application root")

    def get_info(self) -> _InfoDict:
        return {"app": self._app, "prefix": self._prefix}

    async def resolve(self, request: Request) -> _Resolve:
        match_info = await self._app.router.resolve(request)
        match_info.add_app(self._app)
        if isinstance(match_info.http_exception, HTTPMethodNotAllowed):
            methods = match_info.http_exception.allowed_methods
        else:
            methods = set()
        return match_info, methods

    def __len__(self) -> int:
        return len(self._app.router.routes())

    def __iter__(self) -> Iterator[AbstractRoute]:
        return iter(self._app.router.routes())

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Reverse a named concrete route inside the sub-application instead of the sub-app resource.
  2. When iterating resources, skip PrefixedSubAppResource / check isinstance before calling url_for.
  3. Give the target route a name and use app.router.named_resources()['name'].url_for(...).
  4. Build the sub-app URL manually by concatenating the prefix if you truly need the mount point.

Example fix

# before
url = sub_app_resource.url_for()  # raises

# after
# name a route inside the sub-app, reverse that
sub_app.router.add_get('/items', handler, name='items')
url = sub_app.router.named_resources()['items'].url_for()
Defensive patterns

Strategy: type-guard

Validate before calling

from aiohttp.web_urldispatcher import PrefixedSubAppResource, AbstractResource

def safe_url_for(resource, **kw):
    if isinstance(resource, PrefixedSubAppResource):
        raise RuntimeError(f"cannot url_for sub-app resource {resource!r}; reverse a named route inside it")
    return resource.url_for(**kw)

Type guard

from aiohttp.web_urldispatcher import PrefixedSubAppResource

def can_url_for(resource) -> bool:
    return not isinstance(resource, PrefixedSubAppResource)

Try / catch

try:
    url = resource.url_for()
except RuntimeError as e:
    if 'not supported by sub-application' in str(e):
        url = None  # skip sub-app roots in bulk reverse operations
    else:
        raise

Prevention

When it happens

Trigger: Calling app.router.named_resources()['sub_app_name'].url_for(...) or otherwise obtaining the PrefixedSubAppResource for an add_sub_app mount and invoking url_for on it. Also triggered by code iterating resources and calling url_for generically.

Common situations: Generic resource-iteration code that calls url_for() on every resource without checking type; assuming sub-app mounts are reverse-able like named routes; migration from plain routes to sub-apps.

Related errors


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