aio-libs/aiohttp · error · TypeError

Inheritance class from ClientSession is forbidden

Error message

Inheritance class {cls.__name__} from ClientSession is forbidden

What it means

ClientSession.__init_subclass__ unconditionally raises TypeError for any subclass. aiohttp intentionally closes ClientSession for extension because its internal lifecycle, __del__, and middleware plumbing are not safe to override; composition (wrapping a session) is the supported extension model.

Solutions

  1. Use composition: write a class that holds a ClientSession instance and delegates calls.
  2. Set defaults through ClientSession constructor args (base_url, headers, cookies, middlewares).
  3. Use client_middlewares (or a wrapper function) to inject custom request/response behavior.

Example fix

// before
class MySession(ClientSession):
    async def get_json(self, url):
        return await (await self.get(url)).json()
// after
class MyClient:
    def __init__(self, session: ClientSession):
        self._s = session
    async def get_json(self, url):
        async with self._s.get(url) as r:
            return await r.json()
Defensive patterns

Strategy: validation

Validate before calling

# No validation needed if you simply do not subclass.
# Composition pattern:
class ApiClient:
    def __init__(self, session):
        self._s = session

Type guard

from aiohttp import ClientSession

def is_client_session(obj) -> bool:
    return isinstance(obj, ClientSession) and type(obj) is ClientSession

Try / catch

# Subclassing fails at class definition, not catchable in the usual sense.
# Detect at import time:
try:
    class _Sub(ClientSession):  # noqa
        pass
except TypeError:
    # switch to composition
    pass

Prevention

When it happens

Trigger: Writing class MySession(ClientSession): pass anywhere in user code, at class-definition time (not even at instantiation).

Common situations: Porting code from older aiohttp versions or other HTTP libs where session subclassing was idiomatic. Trying to add helper methods or default headers via inheritance instead of composition.

Understand the failure class

Related errors


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

Appendix: source

Thrown at aiohttp/client.py:426

            self._skip_auto_headers = frozenset()

        self._request_class = request_class
        self._response_class = response_class
        self._ws_response_class = ws_response_class

        self._trace_configs = trace_configs or []
        for trace_config in self._trace_configs:
            trace_config.freeze()

        self._resolve_charset = fallback_charset_resolver

        self._default_proxy = proxy
        self._default_ssl = ssl
        self._retry_connection: bool = True
        self._middlewares = tuple(middlewares)

    def __init_subclass__(cls: type["ClientSession"]) -> None:
        raise TypeError(
            f"Inheritance class {cls.__name__} from ClientSession is forbidden"
        )

    def __del__(self, _warnings: Any = warnings) -> None:
        if not self.closed:
            _warnings.warn(
                f"Unclosed client session {self!r}",
                ResourceWarning,
                source=self,
            )
            context = {"client_session": self, "message": "Unclosed client session"}
            if self._source_traceback is not None:
                context["source_traceback"] = self._source_traceback
            self._loop.call_exception_handler(context)

    if sys.version_info >= (3, 11) and TYPE_CHECKING:

        def request(

View on GitHub (pinned to d041d4d0fd)