{"id":"9d9005ec6bae233a","repo":"aio-libs/aiohttp","slug":"cannot-add-frozen-application","errorCode":null,"errorMessage":"Cannot add frozen application","messagePattern":"Cannot add frozen application","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_app.py","lineNumber":290,"sourceCode":"        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)\n\n    def add_routes(self, routes: Iterable[AbstractRouteDef]) -> list[AbstractRoute]:","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/web_app.py#L272-L308","documentation":"Raised by Application._add_subapp when the subapp being added is already frozen. aiohttp pre_freezes a subapp as part of _add_subapp (subapp.pre_freeze()), so passing an already-frozen subapp means it was previously mounted or started elsewhere — reusing it would couple two parents to one router/signals lifecycle, which the framework forbids. Construct a fresh subapp per parent.","triggerScenarios":"Reusing the same subapp instance across two parent apps (`app1.add_subapp('/a', shared); app2.add_subapp('/b', shared)`); adding a subapp that was already started by another runner; caching subapps in a registry and mounting twice.","commonSituations":"Shared service mounted under multiple gateways; test fixtures that reuse one subapp across multiple test apps; plugin objects that wrap an Application and get mounted repeatedly.","solutions":["Construct a new subapp instance for each parent: wrap construction in a factory.","If sharing logic, factor routes/handlers into a function that builds a fresh Application each call.","Audit registries/caches that hand out Application instances — make them factories instead.","Confirm no prior add_subapp or run_app has touched the subapp."],"exampleFix":"// before\nshared = build_admin_app()\napp1.add_subapp('/admin', shared)\napp2.add_subapp('/admin', shared)  # second call -> RuntimeError\n// after\ndef build_admin_app():\n    app = aiohttp.web.Application()\n    app.add_routes(...)\n    return app\napp1.add_subapp('/admin', build_admin_app())\napp2.add_subapp('/admin', build_admin_app())","handlingStrategy":"validation","validationCode":"def safe_add_subapp(parent, prefix, subapp):\n    if getattr(subapp, 'frozen', False):\n        raise RuntimeError('subapp is already frozen; build a fresh instance')\n    return parent.add_subapp(prefix, subapp)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always construct a fresh subapp per parent (use a factory).","Never cache Application instances that will be mounted multiple times.","Audit plugin registries; make them factories, not instance caches."],"tags":["web-app","subapp","lifecycle","reuse"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}