{"id":"dc5170ebea3bea22","repo":"aio-libs/aiohttp","slug":"url-for-is-not-supported-by-sub-application-roo","errorCode":null,"errorMessage":".url_for() is not supported by sub-application root","messagePattern":"\\.url_for\\(\\) is not supported by sub-application root","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_urldispatcher.py","lineNumber":729,"sourceCode":"        super().__init__(prefix)\n        self._app = app\n        self._add_prefix_to_resources(prefix)\n\n    def add_prefix(self, prefix: str) -> None:\n        super().add_prefix(prefix)\n        self._add_prefix_to_resources(prefix)\n\n    def _add_prefix_to_resources(self, prefix: str) -> None:\n        router = self._app.router\n        for resource in router.resources():\n            # Since the canonical path of a resource is about\n            # to change, we need to unindex it and then reindex\n            router.unindex_resource(resource)\n            resource.add_prefix(prefix)\n            router.index_resource(resource)\n\n    def url_for(self, *args: str, **kwargs: str) -> URL:\n        raise RuntimeError(\".url_for() is not supported by sub-application root\")\n\n    def get_info(self) -> _InfoDict:\n        return {\"app\": self._app, \"prefix\": self._prefix}\n\n    async def resolve(self, request: Request) -> _Resolve:\n        match_info = await self._app.router.resolve(request)\n        match_info.add_app(self._app)\n        if isinstance(match_info.http_exception, HTTPMethodNotAllowed):\n            methods = match_info.http_exception.allowed_methods\n        else:\n            methods = set()\n        return match_info, methods\n\n    def __len__(self) -> int:\n        return len(self._app.router.routes())\n\n    def __iter__(self) -> Iterator[AbstractRoute]:\n        return iter(self._app.router.routes())","sourceCodeStart":711,"sourceCodeEnd":747,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/web_urldispatcher.py#L711-L747","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Reverse a named concrete route inside the sub-application instead of the sub-app resource.","When iterating resources, skip PrefixedSubAppResource / check isinstance before calling url_for.","Give the target route a name and use app.router.named_resources()['name'].url_for(...).","Build the sub-app URL manually by concatenating the prefix if you truly need the mount point."],"exampleFix":"# before\nurl = sub_app_resource.url_for()  # raises\n\n# after\n# name a route inside the sub-app, reverse that\nsub_app.router.add_get('/items', handler, name='items')\nurl = sub_app.router.named_resources()['items'].url_for()","handlingStrategy":"type-guard","validationCode":"from aiohttp.web_urldispatcher import PrefixedSubAppResource, AbstractResource\n\ndef safe_url_for(resource, **kw):\n    if isinstance(resource, PrefixedSubAppResource):\n        raise RuntimeError(f\"cannot url_for sub-app resource {resource!r}; reverse a named route inside it\")\n    return resource.url_for(**kw)","typeGuard":"from aiohttp.web_urldispatcher import PrefixedSubAppResource\n\ndef can_url_for(resource) -> bool:\n    return not isinstance(resource, PrefixedSubAppResource)","tryCatchPattern":"try:\n    url = resource.url_for()\nexcept RuntimeError as e:\n    if 'not supported by sub-application' in str(e):\n        url = None  # skip sub-app roots in bulk reverse operations\n    else:\n        raise","preventionTips":["When iterating resources, skip PrefixedSubAppResource before url_for.","Name concrete routes inside sub-apps and reverse those instead.","Keep a registry of reversible route names rather than reversing raw resources."],"tags":["aiohttp","sub-app","url-for","reverse-url","runtime-error"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}