{"id":"694103246839f0b9","repo":"aio-libs/aiohttp","slug":"cannot-add-sub-application-to-frozen-application","errorCode":null,"errorMessage":"Cannot add sub application to frozen application","messagePattern":"Cannot add sub application to frozen application","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_app.py","lineNumber":288,"sourceCode":"\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:\n        if not isinstance(domain, str):\n            raise TypeError(\"Domain must be str\")\n        elif \"*\" in domain:\n            rule: Domain = MaskDomain(domain)\n        else:\n            rule = Domain(domain)\n        factory = partial(MatchedSubAppResource, rule, subapp)\n        return self._add_subapp(factory, subapp)","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/web_app.py#L270-L306","documentation":"Raised by Application._add_subapp when the parent application is already frozen. Once an app has started serving (or was explicitly frozen), its router is immutable because routes are matched against live requests; adding a subapp would alter routing mid-flight. The fix is structural: register subapps before startup, or restart the application with the new topology.","triggerScenarios":"Calling parent_app.add_subapp(...) from inside a request handler or after run_app() has started; adding a subapp in on_startup (which fires during the startup sequence, after pre_freeze); dynamic plugin registration triggered by a request.","commonSituations":"Hot-reloading plugins in a long-running server; lazy mounting in tests that reuse a started app; misordered on_startup handlers where registration runs after freeze; multi-tenant routers added on first tenant.","solutions":["Register all subapps before run_app()/AppRunner.setup() — typically at module load.","For dynamic tenants, use a single catch-all route whose handler dispatches, instead of mutating the router.","If topology must change, tear down the runner and start a new Application.","Move registration into Application construction or a setup() called before serving."],"exampleFix":"// before\nasync def handler(request):\n    request.app.add_subapp('/tenant', tenant_app)  # frozen -> RuntimeError\n// after\napp.add_subapp('/tenant', tenant_app)  # at construction time\nweb.run_app(app)","handlingStrategy":"validation","validationCode":"def safe_add_subapp(parent, prefix, subapp):\n    if getattr(parent, 'frozen', False):\n        raise RuntimeError('parent is frozen; add subapps before startup')\n    return parent.add_subapp(prefix, subapp)","typeGuard":null,"tryCatchPattern":"try:\n    parent.add_subapp(prefix, subapp)\nexcept RuntimeError as e:\n    if 'frozen application' in str(e):\n        # rebuild topology: tear down runner, build new app\n        ...\n    raise","preventionTips":["Mount all subapps at construction time, before run_app().","For dynamic tenants, dispatch inside a handler rather than mutating the router.","Rebuild the Application if topology must change at runtime."],"tags":["web-app","subapp","lifecycle","router"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}