aio-libs/aiohttp · error · RuntimeError

Site is not registered in runner

Error message

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

What it means

Raised by BaseRunner._check_site (called from BaseSite.stop()) when the given site is not in the runner's `_sites` list. stop() needs the registration to exist so it can deregister and close the underlying server; an unregistered site means the stop call is out of order.

Solutions

  1. Only call stop() on sites that were successfully started; track a `started` flag per site.
  2. In cleanup, guard with try/except RuntimeError or check membership first.
  3. Use the runner's cleanup() which handles the registered set atomically.
  4. Avoid double-stop by clearing the site reference after stop().

Example fix

// before
site = TCPSite(runner, host, port)
try:
    await maybe_start(site)
finally:
    await site.stop()  # if start() failed, stop() -> RuntimeError

// after
site = TCPSite(runner, host, port)
started = False
try:
    await site.start()
    started = True
finally:
    if started:
        await site.stop()
Defensive patterns

Strategy: validation

Validate before calling

async def stop_if_started(site, started):
    if site not in started:
        return
    await site.stop()
    started.discard(site)

Try / catch

try:
    await site.stop()
except RuntimeError:
    pass  # site was never started or already stopped

Prevention

When it happens

Trigger: Calling `await site.stop()` on a site that was never started (or already stopped). Also triggered by cleanup code that calls stop() in a finally block when start() was skipped due to an earlier exception.

Common situations: finally/cleanup handlers that unconditionally call stop() even when start() failed; shutdown sequences that stop sites twice; test teardown running against a site that the test never started.

Related errors


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

Appendix: source

Thrown at aiohttp/web_runner.py:393

                # 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__(
        self,
        web_server: Server[BaseRequest],
        *,
        handle_signals: bool = False,
        **kwargs: Any,

View on GitHub (pinned to d041d4d0fd)