aio-libs/aiohttp · error · RuntimeError

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

Error message

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

What it means

BaseRunner._reg_site (line 386-388) rejects registering the same site object twice. Each site's start() calls self._runner._reg_site(self), so starting the same site instance twice (or registering it twice) is a logic error.

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 c0ef574e29)

Solutions

  1. Call site.start() exactly once per site instance.
  2. Create a fresh site object for each (re)start cycle.
  3. Track started state yourself and skip if already started.

Example fix

# before
site = TCPSite(runner, '0.0.0.0', 8080)
await site.start()
await site.start()  # second start re-registers -> RuntimeError

# after
site = TCPSite(runner, '0.0.0.0', 8080)
await site.start()  # only once
Defensive patterns

Strategy: validation

Validate before calling

started = set()
async def start_once(site, runner, started):
    if site in runner.sites or id(site) in started:
        return
    await site.start()
    started.add(id(site))

Prevention

When it happens

Trigger: Calling `await site.start()` twice on the same TCPSite/UnixSite/SockSite/NamedPipeSite instance; reusing a site object after stop() without re-creating it.

Common situations: Restart logic that reuses site objects; calling start() in a retry loop; double-await from concurrent tasks.

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/0393d3a420d38be5.json. Report an issue: GitHub.