aio-libs/aiohttp · error · RuntimeError
Call runner.setup() before making a site
Error message
Call runner.setup() before making a site
What it means
Raised by BaseSite.__init__ (TCPSite, UnixSite, NamedPipeSite, etc.) when the supplied runner's `server` attribute is None, meaning runner.setup() was never awaited. Sites bind the runner's already-built server to a socket, so the server must exist first.
Solutions
- Always `await runner.setup()` BEFORE constructing any Site: `await runner.setup(); site = TCPSite(runner, host, port); await site.start()`.
- Use the `AppRunner` or `ServerRunner` lifecycle helpers in the documented order.
- In tests, run setup() inside the async test body, not in __init__.
- Use `web.run_app(app)` for the common case — it handles ordering internally.
Example fix
// before runner = AppRunner(app) site = TCPSite(runner, '0.0.0.0', 8080) # RuntimeError: server is None await runner.setup() await site.start() // after runner = AppRunner(app) await runner.setup() site = TCPSite(runner, '0.0.0.0', 8080) await site.start()
Defensive patterns
Strategy: validation
Validate before calling
async def make_site(runner, *args, **kw):
if runner.server is None:
await runner.setup()
return TCPSite(runner, *args, **kw) Type guard
def runner_is_ready(runner) -> bool:
return runner.server is not None Prevention
- Always await runner.setup() before constructing any Site.
- Use web.run_app() for the common case — it handles ordering.
- Keep setup() and Site construction in the same async function.
- In tests, run setup() inside the async test body.
When it happens
Trigger: Constructing `TCPSite(runner)` (or any BaseSite subclass) before `await runner.setup()`. Common in code that builds the site eagerly at import time or in a synchronous helper, then awaits setup() later.
Common situations: Application factory ordering bugs where Site construction is moved before setup(); refactoring that splits setup() and start() across functions and swaps their order; testing harness that builds sites in setUp before the loop runs setup().
Related errors
- Site is already registered in runner
- Site is not registered in runner
- Cannot call write() after write_eof()
- Cannot call write() before prepare()
- Named Pipes only available in proactor loop under windows
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/aac0922c4ae7ebc4.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/web_runner.py:103
code = 1
def _raise_graceful_exit() -> None:
raise GracefulExit()
class BaseSite(ABC):
__slots__ = ("_runner", "_ssl_context", "_backlog", "_server")
def __init__(
self,
runner: "BaseRunner[Any]",
*,
ssl_context: SSLContext | None = None,
backlog: int = 128,
) -> None:
if runner.server is None:
raise RuntimeError("Call runner.setup() before making a site")
self._runner = runner
self._ssl_context = ssl_context
self._backlog = backlog
self._server: asyncio.Server | None = None
@property
@abstractmethod
def name(self) -> str:
"""Return the name of the site (e.g. a URL)."""
@abstractmethod
async def start(self) -> None:
self._runner._reg_site(self)
async def stop(self) -> None:
self._runner._check_site(self)
if self._server is not None: # Maybe not started yet
self._server.close()View on GitHub (pinned to d041d4d0fd)