aio-libs/aiohttp · error · TypeError

The first argument should be web.Application instance, got

Error message

The first argument should be web.Application instance, got {app!r}

What it means

AppRunner.__init__ requires its first positional argument to be an instance of aiohttp.web.Application. The constructor performs an isinstance(app, Application) check at entry and raises TypeError if it fails, because every downstream step (handler_args extraction, super().__init__ setup, self._app assignment) assumes a real Application object. Passing anything else — a string, a dict, a Router, a None — would cause cryptic AttributeError failures later, so aiohttp fails fast with a descriptive message.

Solutions

  1. Ensure the first argument is an instance of aiohttp.web.Application — call web.Application() to create one if needed.
  2. If you are passing app.router, change it to the Application instance itself.
  3. In tests, construct a real Application (web.Application()) rather than a mock or a bare dict.
  4. If subclassing Application, confirm the subclass derives from aiohttp.web.Application.

Example fix

// before
runner = web.AppRunner(router)  # router is app.router
// after
runner = web.AppRunner(app)    # pass the Application instance
Defensive patterns

Strategy: type-guard

Validate before calling

from aiohttp import web

def make_runner(app) -> web.AppRunner:
    if not isinstance(app, web.Application):
        raise TypeError(f'Expected web.Application, got {type(app).__name__}')
    return web.AppRunner(app)

Type guard

from aiohttp import web

def is_application(app) -> bool:
    return isinstance(app, web.Application)

Try / catch

try:
    runner = web.AppRunner(app)
except TypeError as e:
    if 'should be web.Application' in str(e):
        # log and abort startup
        raise
    raise

Prevention

When it happens

Trigger: Constructing AppRunner directly (AppRunner(app)) with a non-Application object: e.g. AppRunner(router), AppRunner(None), AppRunner('my-app'), or passing a sub-app (the nested Application added via add_subapp) when the outer Application is intended. Also triggered by passing an Application subclass that does not actually inherit from aiohttp.web.Application.

Common situations: Mistaking web.AppRunner for a generic runner that accepts a Router object; copy-pasting from examples that build the Application in a separate function and forgetting the call; test fixtures that return a mock or a partially-built object; refactoring that accidentally passes the app's .router attribute instead of the app itself.

Related errors


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

Appendix: source

Thrown at aiohttp/web_runner.py:440

    async def _cleanup_server(self) -> None:
        pass


class AppRunner(BaseRunner[Request]):
    """Web Application runner"""

    __slots__ = ("_app",)

    def __init__(
        self,
        app: Application,
        *,
        handle_signals: bool = False,
        access_log_class: type[AbstractAccessLogger] = AccessLogger,
        **kwargs: Any,
    ) -> None:
        if not isinstance(app, Application):
            raise TypeError(
                f"The first argument should be web.Application instance, got {app!r}"
            )
        kwargs["access_log_class"] = access_log_class

        if app._handler_args:
            for k, v in app._handler_args.items():
                kwargs[k] = v

        if not issubclass(kwargs["access_log_class"], AbstractAccessLogger):
            raise TypeError(
                "access_log_class must be subclass of "
                "aiohttp.abc.AbstractAccessLogger, got {}".format(
                    kwargs["access_log_class"]
                )
            )

        super().__init__(handle_signals=handle_signals, **kwargs)
        self._app = app

View on GitHub (pinned to d041d4d0fd)