{"id":"34eeff23cd12e462","repo":"aio-libs/aiohttp","slug":"site-site-is-not-registered-in-runner-self","errorCode":null,"errorMessage":"Site {site} is not registered in runner {self}","messagePattern":"Site (.+?) is not registered in runner (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_runner.py","lineNumber":393,"sourceCode":"                # remove_signal_handler is not implemented on Windows\n                pass\n\n    @abstractmethod\n    async def _make_server(self) -> Server[_Request]:\n        \"\"\"Return a new server for the runner to serve requests.\"\"\"\n\n    @abstractmethod\n    async def _cleanup_server(self) -> None:\n        \"\"\"Run any cleanup steps after the server is shutdown.\"\"\"\n\n    def _reg_site(self, site: BaseSite) -> None:\n        if site in self._sites:\n            raise RuntimeError(f\"Site {site} is already registered in runner {self}\")\n        self._sites.append(site)\n\n    def _check_site(self, site: BaseSite) -> None:\n        if site not in self._sites:\n            raise RuntimeError(f\"Site {site} is not registered in runner {self}\")\n\n    def _unreg_site(self, site: BaseSite) -> None:\n        if site not in self._sites:\n            raise RuntimeError(f\"Site {site} is not registered in runner {self}\")\n        self._sites.remove(site)\n\n\nclass ServerRunner(BaseRunner[BaseRequest]):\n    \"\"\"Low-level web server runner\"\"\"\n\n    __slots__ = (\"_web_server\",)\n\n    def __init__(\n        self,\n        web_server: Server[BaseRequest],\n        *,\n        handle_signals: bool = False,\n        **kwargs: Any,","sourceCodeStart":375,"sourceCodeEnd":411,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/web_runner.py#L375-L411","documentation":"BaseRunner._check_site (line 391-393) is called by BaseSite.stop() (line 119). If the site is not in the runner's _sites list (i.e. it was never registered/started, or already unregistered), stop() raises RuntimeError rather than silently no-oping.","triggerScenarios":"Calling `await site.stop()` on a site that was never started (start() registers it), or stopping a site twice (after stop() the first time does not auto-unregister, but if cleanup ran it would).","commonSituations":"Cleanup/finally blocks that call stop() unconditionally even when start() failed or never ran; shutdown code re-stopping sites.","solutions":["Only call stop() on sites you successfully started (track a started flag).","Guard with a check: if site in runner.sites: await site.stop().","Let runner.cleanup() handle stopping all registered sites instead of manual stop()."],"exampleFix":"# before\nsite = TCPSite(runner, '0.0.0.0', 8080)\n# start() never awaited\nawait site.stop()  # raises RuntimeError (not registered)\n\n# after\nsite = TCPSite(runner, '0.0.0.0', 8080)\nawait site.start()\nawait site.stop()","handlingStrategy":"validation","validationCode":"async def safe_stop(site, runner):\n    if site in runner.sites:\n        await site.stop()","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only stop sites you successfully started.","Prefer runner.cleanup() to stop all registered sites.","Guard stop() calls with a membership check against runner.sites."],"tags":["server","runner","site","lifecycle","shutdown"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}