{"record":{"id":"aac0922c4ae7ebc4","repo":"aio-libs/aiohttp","slug":"call-runner-setup-before-making-a-site","errorCode":null,"errorMessage":"Call runner.setup() before making a site","messagePattern":"Call runner\\.setup\\(\\) before making a site","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_runner.py","lineNumber":103,"sourceCode":"    code = 1\n\n\ndef _raise_graceful_exit() -> None:\n    raise GracefulExit()\n\n\nclass BaseSite(ABC):\n    __slots__ = (\"_runner\", \"_ssl_context\", \"_backlog\", \"_server\")\n\n    def __init__(\n        self,\n        runner: \"BaseRunner[Any]\",\n        *,\n        ssl_context: SSLContext | None = None,\n        backlog: int = 128,\n    ) -> None:\n        if runner.server is None:\n            raise RuntimeError(\"Call runner.setup() before making a site\")\n        self._runner = runner\n        self._ssl_context = ssl_context\n        self._backlog = backlog\n        self._server: asyncio.Server | None = None\n\n    @property\n    @abstractmethod\n    def name(self) -> str:\n        \"\"\"Return the name of the site (e.g. a URL).\"\"\"\n\n    @abstractmethod\n    async def start(self) -> None:\n        self._runner._reg_site(self)\n\n    async def stop(self) -> None:\n        self._runner._check_site(self)\n        if self._server is not None:  # Maybe not started yet\n            self._server.close()","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/web_runner.py#L85-L121","documentation":"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.","triggerScenarios":"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.","commonSituations":"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().","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."],"exampleFix":"// before\nrunner = AppRunner(app)\nsite = TCPSite(runner, '0.0.0.0', 8080)  # RuntimeError: server is None\nawait runner.setup()\nawait site.start()\n\n// after\nrunner = AppRunner(app)\nawait runner.setup()\nsite = TCPSite(runner, '0.0.0.0', 8080)\nawait site.start()","handlingStrategy":"validation","validationCode":"async def make_site(runner, *args, **kw):\n    if runner.server is None:\n        await runner.setup()\n    return TCPSite(runner, *args, **kw)","typeGuard":"def runner_is_ready(runner) -> bool:\n    return runner.server is not None","tryCatchPattern":null,"preventionTips":["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."],"tags":["aiohttp","web-runner","lifecycle","site","ordering"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}