aio-libs/aiohttp · error · RuntimeError

Site is already registered in runner

Error message

Site {site} is already registered in runner {self}

What it means

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.

Solutions

  1. Track started sites and skip re-starting: `if site not in runner._sites: await site.start()` (or maintain your own set).
  2. Call `await site.stop()` before re-calling `start()`.
  3. Use a fresh runner per lifecycle rather than reusing one across restarts.
  4. Guard restart loops with an idempotent wrapper that checks site registration.

Example fix

// before
for site in sites:
    await site.start()  # if sites list is reused -> RuntimeError on 2nd pass

// after
started = set()
for site in sites:
    if site not in started:
        await site.start()
        started.add(site)
Defensive patterns

Strategy: validation

Validate before calling

async def start_once(site, started):
    if site in started:
        return
    await site.start()
    started.add(site)

Try / catch

try:
    await site.start()
except RuntimeError:
    pass  # already registered; safe to ignore during reload

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/0393d3a420d38be5. Report an issue: GitHub.

Appendix: source

Thrown at aiohttp/web_runner.py:388

            loop = asyncio.get_running_loop()
            try:
                loop.remove_signal_handler(signal.SIGINT)
                loop.remove_signal_handler(signal.SIGTERM)
            except NotImplementedError:
                # remove_signal_handler is not implemented on Windows
                pass

    @abstractmethod
    async def _make_server(self) -> Server[_Request]:
        """Return a new server for the runner to serve requests."""

    @abstractmethod
    async def _cleanup_server(self) -> None:
        """Run any cleanup steps after the server is shutdown."""

    def _reg_site(self, site: BaseSite) -> None:
        if site in self._sites:
            raise RuntimeError(f"Site {site} is already registered in runner {self}")
        self._sites.append(site)

    def _check_site(self, site: BaseSite) -> None:
        if site not in self._sites:
            raise RuntimeError(f"Site {site} is not registered in runner {self}")

    def _unreg_site(self, site: BaseSite) -> None:
        if site not in self._sites:
            raise RuntimeError(f"Site {site} is not registered in runner {self}")
        self._sites.remove(site)


class ServerRunner(BaseRunner[BaseRequest]):
    """Low-level web server runner"""

    __slots__ = ("_web_server",)

    def __init__(

View on GitHub (pinned to d041d4d0fd)