{"record":{"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/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/web_app.py#L262-L298","documentation":"Raised by Application.add_subapp when the prefix, after rstrip('/'), is empty. A subapp must mount under a non-empty path so the router can distinguish its routes from the parent's; an empty prefix would make the subapp shadow the entire parent application. Both '' and '/' (which strips to '') are rejected.","triggerScenarios":"Calling app.add_subapp('', subapp) or app.add_subapp('/', subapp); a prefix built from an empty config value; constructing the prefix by stripping a slash that was the only character.","commonSituations":"Config-driven mounting where the prefix field was left blank; attempting to mount a subapp at the root (use the parent's own routes instead); off-by-one in path joining that drops the prefix.","solutions":["Provide a non-empty, non-slash prefix, e.g. app.add_subapp('/api', subapp).","Validate at config load: if not prefix.strip('/'): raise ValueError('prefix required').","If you want the subapp at root, merge its routes into the parent application directly instead of using add_subapp."],"exampleFix":"# before\napp.add_subapp(mount_path, subapp)  # mount_path == '/'\n\n# after\nmount = mount_path.rstrip('/') or '/api'\napp.add_subapp(mount, subapp)","handlingStrategy":"validation","validationCode":"if not prefix.strip('/'):\n    raise ValueError('prefix must not be empty')\napp.add_subapp(prefix, subapp)","typeGuard":"def nonempty_prefix(p: str) -> bool:\n    return isinstance(p, str) and p.strip('/') != ''","tryCatchPattern":"try:\n    app.add_subapp(prefix, subapp)\nexcept ValueError as e:\n    if 'empty' in str(e):\n        app.add_subapp('/api', subapp)\n    else:\n        raise","preventionTips":["Validate prefix has non-slash content before mounting.","Reject blank prefix values in configuration.","Mount at root by merging routes, not via add_subapp."],"tags":["web","subapp","routing","validation"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}