{"id":"db434ee0c26fe2b5","repo":"aio-libs/aiohttp","slug":"prefix-cannot-be-empty","errorCode":null,"errorMessage":"Prefix cannot be empty","messagePattern":"Prefix cannot be empty","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_app.py","lineNumber":280,"sourceCode":"        def reg_handler(signame: str) -> None:\n            subsig = getattr(subapp, signame)\n\n            async def handler(app: \"Application\") -> None:\n                await subsig.send(subapp)\n\n            appsig = getattr(self, signame)\n            appsig.append(handler)\n\n        reg_handler(\"on_startup\")\n        reg_handler(\"on_shutdown\")\n        reg_handler(\"on_cleanup\")\n\n    def add_subapp(self, prefix: str, subapp: \"Application\") -> PrefixedSubAppResource:\n        if not isinstance(prefix, str):\n            raise TypeError(\"Prefix must be str\")\n        prefix = prefix.rstrip(\"/\")\n        if not prefix:\n            raise ValueError(\"Prefix cannot be empty\")\n        factory = partial(PrefixedSubAppResource, prefix, subapp)\n        return self._add_subapp(factory, subapp)\n\n    def _add_subapp(\n        self, resource_factory: Callable[[], _Resource], subapp: \"Application\"\n    ) -> _Resource:\n        if self.frozen:\n            raise RuntimeError(\"Cannot add sub application to frozen application\")\n        if subapp.frozen:\n            raise RuntimeError(\"Cannot add frozen application\")\n        resource = resource_factory()\n        self.router.register_resource(resource)\n        self._reg_subapp_signals(subapp)\n        self._subapps.append(subapp)\n        subapp.pre_freeze()\n        return resource\n\n    def add_domain(self, domain: str, subapp: \"Application\") -> MatchedSubAppResource:","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/web_app.py#L262-L298","documentation":"Raised by Application.add_subapp when prefix, after rstrip('/'), is empty. A subapp must be scoped under a non-empty path segment; mounting at '' would intercept every request including ones meant for the parent, which is ambiguous and almost never intended. aiohttp rejects this with ValueError rather than guessing the user meant the root.","triggerScenarios":"Calling app.add_subapp('', subapp); add_subapp('/', subapp) (becomes '' after rstrip); add_subapp('///', subapp) (also collapses to ''); dynamically computed prefix that resolves to root.","commonSituations":"Default value of an optional config prefix; templating that yields '/' for the root deployment; refactoring that forgot to set the prefix; attempt to mount a default subapp at root.","solutions":["Provide a non-empty, non-/ prefix: '/v1', '/admin', etc.","If you actually want everything routed through one app, do not use add_subapp — configure that app directly.","Validate at config load: `if not prefix or prefix == '/': raise ValueError('non-root prefix required')`."],"exampleFix":"// before\napp.add_subapp('/', subapp)  # ValueError\n// after\napp.add_subapp('/api', subapp)","handlingStrategy":"validation","validationCode":"def safe_add_subapp(app, prefix, subapp):\n    if not isinstance(prefix, str):\n        raise TypeError('prefix must be str')\n    prefix = prefix.rstrip('/')\n    if not prefix:\n        raise ValueError('prefix cannot be empty')\n    return app.add_subapp(prefix, subapp)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Require a non-root, non-empty path segment for subapps.","Validate prefix at config load (not '' or '/').","If you need root routing, configure the parent app directly."],"tags":["web-app","subapp","validation"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}