{"id":"8f94012d3a59e87e","repo":"aio-libs/aiohttp","slug":"cannot-change-apps-stack-after-freeze-call","errorCode":null,"errorMessage":"Cannot change apps stack after .freeze() call","messagePattern":"Cannot change apps stack after \\.freeze\\(\\) call","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_urldispatcher.py","lineNumber":249,"sourceCode":"\n    @property\n    def expect_handler(self) -> _ExpectHandler:\n        return self._route.handle_expect_header\n\n    @property\n    def http_exception(self) -> HTTPException | None:\n        return None\n\n    def get_info(self) -> _InfoDict:  # type: ignore[override]\n        return self._route.get_info()\n\n    @property\n    def apps(self) -> tuple[\"Application\", ...]:\n        return tuple(self._apps)\n\n    def add_app(self, app: \"Application\") -> None:\n        if self._frozen:\n            raise RuntimeError(\"Cannot change apps stack after .freeze() call\")\n        if self._current_app is None:\n            self._current_app = app\n        self._apps.insert(0, app)\n\n    @property\n    def current_app(self) -> \"Application\":\n        app = self._current_app\n        assert app is not None\n        return app\n\n    @current_app.setter\n    def current_app(self, app: \"Application\") -> None:\n        if DEBUG:\n            if app not in self._apps:\n                raise RuntimeError(\n                    f\"Expected one of the following apps {self._apps!r}, got {app!r}\"\n                )\n        self._current_app = app","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/web_urldispatcher.py#L231-L267","documentation":"Raised as RuntimeError by UrlMappingMatchInfo.add_app when an application is pushed onto the match-info's app stack after freeze() was called. The app stack records the chain of sub-applications traversed during routing; once frozen (after startup), it is immutable, so late additions indicate routing happening after the app lifecycle entered its frozen phase.","triggerScenarios":"Internally triggered when a sub-application resource tries to add_app after the match info is frozen — typically a sign of routing against an Application whose cleanup/startup re-entered resolution, or a custom AbstractResource that resolves after freeze. Not normally raised by user code directly.","commonSituations":"Custom router/resource subclasses that call add_app outside the normal request routing window; using add_sub_app after app.freeze()/startup; re-using a frozen Application's match info; bugs in middleware that re-resolve routes.","solutions":["Ensure sub-applications are added via app.add_sub_app(prefix, sub_app) BEFORE app.freeze()/startup, not during request handling.","Do not call match_info.add_app(...) in custom resource resolve() implementations after the app is frozen.","If using a custom AbstractResource, return a fresh UrlMappingMatchInfo rather than reusing a frozen one.","Verify you are not re-freezing or re-starting an already-started Application."],"exampleFix":"# before (adding sub-app after startup)\nrunner = web.AppRunner(app)\nawait runner.setup()\napp.add_sub_app('/sub', sub_app)  # too late\n\n# after\napp.add_sub_app('/sub', sub_app)\nrunner = web.AppRunner(app)\nawait runner.setup()","handlingStrategy":"validation","validationCode":"# Prevent by structuring setup: never add sub-apps after runner.setup()\ndef setup_app(app, sub_apps):\n    for prefix, sub in sub_apps:\n        app.add_sub_app(prefix, sub)  # before any freeze/startup\n    return app","typeGuard":null,"tryCatchPattern":"try:\n    app.add_sub_app(prefix, sub_app)\nexcept RuntimeError as e:\n    if 'freeze' in str(e):\n        raise RuntimeError(\"App already frozen; add sub-apps before startup\") from e\n    raise","preventionTips":["Add all sub-applications before calling runner.setup()/startup.","Do not reuse frozen Applications across setups.","Treat add_sub_app after startup as a configuration bug."],"tags":["aiohttp","routing","sub-app","lifecycle","runtime-error"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}