{"record":{"id":"0393d3a420d38be5","repo":"aio-libs/aiohttp","slug":"site-site-is-already-registered-in-runner-self","errorCode":null,"errorMessage":"Site {site} is already registered in runner {self}","messagePattern":"Site (.+?) is already registered in runner (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_runner.py","lineNumber":388,"sourceCode":"            loop = asyncio.get_running_loop()\n            try:\n                loop.remove_signal_handler(signal.SIGINT)\n                loop.remove_signal_handler(signal.SIGTERM)\n            except NotImplementedError:\n                # 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__(","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/web_runner.py#L370-L406","documentation":"Raised by BaseRunner._reg_site when the given site instance is already in the runner's `_sites` list. Each site can be registered with a given runner at most once; double registration would start two servers bound to the same handle.","triggerScenarios":"Calling `await site.start()` twice on the same site without an intervening `stop()`. Or constructing two sites and registering the same site object twice through a buggy loop.","commonSituations":"Restart logic that calls start() again after a transient failure; reloading configuration that re-iterates a sites list including already-started sites; tests that reuse a runner across cases without stopping prior sites.","solutions":["Track started sites and skip re-starting: `if site not in runner._sites: await site.start()` (or maintain your own set).","Call `await site.stop()` before re-calling `start()`.","Use a fresh runner per lifecycle rather than reusing one across restarts.","Guard restart loops with an idempotent wrapper that checks site registration."],"exampleFix":"// before\nfor site in sites:\n    await site.start()  # if sites list is reused -> RuntimeError on 2nd pass\n\n// after\nstarted = set()\nfor site in sites:\n    if site not in started:\n        await site.start()\n        started.add(site)","handlingStrategy":"validation","validationCode":"async def start_once(site, started):\n    if site in started:\n        return\n    await site.start()\n    started.add(site)","typeGuard":null,"tryCatchPattern":"try:\n    await site.start()\nexcept RuntimeError:\n    pass  # already registered; safe to ignore during reload","preventionTips":["Track started sites in a set; skip re-starting.","Call site.stop() before re-calling start().","Use a fresh runner across restart cycles.","Idempotently wrap site start in restart logic."],"tags":["aiohttp","web-runner","site","registration","lifecycle"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}