Skip to content

aiohttp 3.14 instance middleware silently doesn't dispatch new-style

Symptom

A @web.middleware-decorated class instance registered in web.Application(middlewares=[...]) is never invoked: requests bypass auth entirely, or the middleware raises TypeError: object is not callable — with no startup warning. In FestIn, the JWT middleware silently stopped protecting routes after an aiohttp upgrade.

Cause

aiohttp 3.9+ dispatches new-style middleware only when the marker is visible on the instance. @web.middleware decorates __call__ of the class, but a decorated instance loses the metadata that the dispatcher looks for.

festin/service/serve.py — the working pattern:

def _wrap_middleware(instance):
    @web.middleware
    async def _mw(request, handler):
        # pre-processing
        return await instance.__call__(request, handler)
    return _mw

Also required on the class: __middleware_version__ = 1 (see festin/service/auth.py, JWTMiddleware).

Contraste

Tested live against aiohttp 3.14: instance-registered middleware never executed; the wrapped-function form executed on every request.

How to avoid it

Never register middleware instances directly. Always wrap them with _wrap_middleware() (or define middlewares as plain functions). Two real bugs came from this: silent auth bypass and "Response object is not callable".