{"id":"c49298eb45578a92","repo":"aio-libs/aiohttp","slug":"wsgi-app-should-be-either-application-or-async-fun","errorCode":null,"errorMessage":"wsgi app should be either Application or async function returning Application, got {self.wsgi}","messagePattern":"wsgi app should be either Application or async function returning Application, got (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"critical","filePath":"aiohttp/worker.py","lineNumber":83,"sourceCode":"            self.loop.close()\n\n        sys.exit(self.exit_code)\n\n    async def _run(self) -> None:\n        runner = None\n        if isinstance(self.wsgi, Application):\n            app = self.wsgi\n        elif inspect.iscoroutinefunction(self.wsgi) or (\n            sys.version_info < (3, 14) and asyncio.iscoroutinefunction(self.wsgi)  # type: ignore[deprecated]\n        ):\n            wsgi = await self.wsgi()\n            if isinstance(wsgi, web.AppRunner):\n                runner = wsgi\n                app = runner.app\n            else:\n                app = wsgi\n        else:\n            raise RuntimeError(\n                \"wsgi app should be either Application or \"\n                f\"async function returning Application, got {self.wsgi}\"\n            )\n\n        if runner is None:\n            access_log = self.log.access_log if self.cfg.accesslog else None\n            runner = web.AppRunner(\n                app,\n                logger=self.log,\n                keepalive_timeout=self.cfg.keepalive,\n                access_log=access_log,\n                access_log_format=self._get_valid_log_format(\n                    self.cfg.access_log_format\n                ),\n                shutdown_timeout=self.cfg.graceful_timeout / 100 * 95,\n            )\n        await runner.setup()\n","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/worker.py#L65-L101","documentation":"Raised by GunicornWebWorker._run() when the `wsgi` (the app entry configured for the gunicorn worker) is neither an aiohttp.web.Application instance nor an async callable that returns an Application (or AppRunner). The worker needs a concrete Application to wrap in an AppRunner and bind to sockets, so anything else is rejected at startup.","triggerScenarios":"Configuring gunicorn with a module path that resolves to a plain function, a sync function, a class, None, or a string, instead of an Application or async factory. The else branch at aiohttp/worker.py:82-86 fires after the isinstance and iscoroutinefunction checks fail.","commonSituations":"Pointing gunicorn `-w`/`--module` at a module whose only callable is a sync factory; an app factory that returns an AppRunner is fine but one returning a coroutine-of-something-else is not; passing the Application class instead of an instance; a typo in the gunicorn app module spec.","solutions":["Expose an `app = web.Application(...)` module-level instance, or an `async def app_factory(): return web.Application(...)`.","Ensure the factory is `async def` (the worker awaits it); a plain `def` returning Application is not accepted.","Double-check the gunicorn module spec points to the module containing the app/factory, not a sub-attribute that is not an Application.","If returning an AppRunner from the factory, return it directly (the worker detects web.AppRunner)."],"exampleFix":"# before (module passed to gunicorn)\ndef app():\n    return web.Application()  # sync, not accepted\n\n# after\napp = web.Application()\n# or\nasync def app():\n    return web.Application()","handlingStrategy":"type-guard","validationCode":"import inspect\nfrom aiohttp import web\n\ndef validate_app(app):\n    if isinstance(app, web.Application):\n        return app\n    if inspect.iscoroutinefunction(app):\n        return app  # async factory\n    raise TypeError(\"app must be Application or async factory\")","typeGuard":"import inspect\nfrom aiohttp import web\n\ndef is_valid_app_entry(obj) -> bool:\n    return isinstance(obj, web.Application) or inspect.iscoroutinefunction(obj)","tryCatchPattern":"try:\n        worker._run()\nexcept RuntimeError as e:\n    if \"wsgi app should be\" in str(e):\n        # fix the configured app module to expose Application/async factory\n        raise\n    raise","preventionTips":["Expose `app = web.Application(...)` or `async def app()` at module level for gunicorn.","Ensure factories are async def; sync factories are rejected.","Validate the app object type in tests before deploying with gunicorn."],"tags":["gunicorn","worker","deployment","config"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}