aio-libs/aiohttp · error · RuntimeError
Cannot add sub application to frozen application
Error message
Cannot add sub application to frozen application
What it means
Raised by Application._add_subapp when the parent application is already frozen. Once an app is frozen (during startup or when itself added as a subapp), its router and subapp list are immutable, so adding a new subapp would not be reflected in the live routing table and could violate the freeze contract. The check uses self.frozen.
Solutions
- Register all subapps before starting the server, i.e. at module load time or in an on_startup handler that runs before freeze.
- If dynamic mounting is required, restructure so subapps are known at startup, or rebuild and restart the app.
- Check app.frozen before calling add_subapp and surface a clear error.
Example fix
# before
async def late_startup(app):
app.add_subapp('/late', sub) # parent already frozen
app.on_startup.append(late_startup)
# after
app.add_subapp('/late', sub) # register at module load, before run_app Defensive patterns
Strategy: validation
Validate before calling
if app.frozen:
raise RuntimeError('cannot add subapp after startup')
app.add_subapp(prefix, subapp) Type guard
def app_accepts_subapps(app) -> bool:
return not getattr(app, 'frozen', False) Prevention
- Register all subapps at module load time, before run_app.
- Order on_startup callbacks so mounting happens pre-freeze.
- Never call add_subapp from request handlers.
When it happens
Trigger: Calling app.add_subapp(...) after web.run_app has started, or inside an on_startup handler that runs after freeze, or on an app that was already attached as a subapp elsewhere.
Common situations: Dynamically registering sub-apps from startup hooks that fire post-freeze; attempting to add routes/subapps from within a request handler; ordering on_startup callbacks such that add_subapp runs after the app freeze step.
Related errors
- Cannot add frozen application
- Changing state of started or joined application is forbidden
- Prefix cannot be empty
- Prefix must be str
- Already started
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/694103246839f0b9.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/web_app.py:288
reg_handler("on_startup")
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)View on GitHub (pinned to d041d4d0fd)