aio-libs/aiohttp · error · RuntimeError
Cannot add frozen application
Error message
Cannot add frozen application
What it means
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.
Source
Thrown at aiohttp/web_app.py:290
reg_handler("on_shutdown")
reg_handler("on_cleanup")
def add_subapp(self, prefix: str, subapp: "Application") -> PrefixedSubAppResource:
if not isinstance(prefix, str):
raise TypeError("Prefix must be str")
prefix = prefix.rstrip("/")
if not prefix:
raise ValueError("Prefix cannot be empty")
factory = partial(PrefixedSubAppResource, prefix, subapp)
return self._add_subapp(factory, subapp)
def _add_subapp(
self, resource_factory: Callable[[], _Resource], subapp: "Application"
) -> _Resource:
if self.frozen:
raise RuntimeError("Cannot add sub application to frozen application")
if subapp.frozen:
raise RuntimeError("Cannot add frozen application")
resource = resource_factory()
self.router.register_resource(resource)
self._reg_subapp_signals(subapp)
self._subapps.append(subapp)
subapp.pre_freeze()
return resource
def add_domain(self, domain: str, subapp: "Application") -> MatchedSubAppResource:
if not isinstance(domain, str):
raise TypeError("Domain must be str")
elif "*" in domain:
rule: Domain = MaskDomain(domain)
else:
rule = Domain(domain)
factory = partial(MatchedSubAppResource, rule, subapp)
return self._add_subapp(factory, subapp)
def add_routes(self, routes: Iterable[AbstractRouteDef]) -> list[AbstractRoute]:View on GitHub (pinned to c0ef574e29)
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.
Example fix
// before
shared = build_admin_app()
app1.add_subapp('/admin', shared)
app2.add_subapp('/admin', shared) # second call -> RuntimeError
// after
def build_admin_app():
app = aiohttp.web.Application()
app.add_routes(...)
return app
app1.add_subapp('/admin', build_admin_app())
app2.add_subapp('/admin', build_admin_app()) Defensive patterns
Strategy: validation
Validate before calling
def safe_add_subapp(parent, prefix, subapp):
if getattr(subapp, 'frozen', False):
raise RuntimeError('subapp is already frozen; build a fresh instance')
return parent.add_subapp(prefix, subapp) Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Cannot add sub application to frozen application
- Changing state of started or joined application is forbidden
- Prefix must be str
- Prefix cannot be empty
- Domain must be str
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/9d9005ec6bae233a.json.
Report an issue: GitHub.