{"id":"734981865e21dec5","repo":"aio-libs/aiohttp","slug":"inheritance-class-cls-from-clientsession-is-forb","errorCode":null,"errorMessage":"Inheritance class {cls} from ClientSession is forbidden","messagePattern":"Inheritance class (.+?) from ClientSession is forbidden","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"aiohttp/client.py","lineNumber":426,"sourceCode":"            self._skip_auto_headers = frozenset()\n\n        self._request_class = request_class\n        self._response_class = response_class\n        self._ws_response_class = ws_response_class\n\n        self._trace_configs = trace_configs or []\n        for trace_config in self._trace_configs:\n            trace_config.freeze()\n\n        self._resolve_charset = fallback_charset_resolver\n\n        self._default_proxy = proxy\n        self._default_ssl = ssl\n        self._retry_connection: bool = True\n        self._middlewares = tuple(middlewares)\n\n    def __init_subclass__(cls: type[\"ClientSession\"]) -> None:\n        raise TypeError(\n            f\"Inheritance class {cls.__name__} from ClientSession is forbidden\"\n        )\n\n    def __del__(self, _warnings: Any = warnings) -> None:\n        if not self.closed:\n            _warnings.warn(\n                f\"Unclosed client session {self!r}\",\n                ResourceWarning,\n                source=self,\n            )\n            context = {\"client_session\": self, \"message\": \"Unclosed client session\"}\n            if self._source_traceback is not None:\n                context[\"source_traceback\"] = self._source_traceback\n            self._loop.call_exception_handler(context)\n\n    if sys.version_info >= (3, 11) and TYPE_CHECKING:\n\n        def request(","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/client.py#L408-L444","documentation":"Raised by `ClientSession.__init_subclass__` (client.py:425-428). aiohttp deliberately forbids subclassing ClientSession because its internals (Cython extensions, connector ownership, lifecycle) assume the concrete class. The hook fires automatically the moment Python sees a subclass defined, so the error surfaces at class-definition/import time, not at instantiation.","triggerScenarios":"Writing `class MySession(aiohttp.ClientSession): ...` anywhere in the codebase triggers it on import. Also hit by metaclass-based frameworks or mixins that auto-derive from ClientSession.","commonSituations":"Migrating from `requests` where subclassing `Session` to add defaults/headers is idiomatic; trying to add logging or retry methods via inheritance; DI frameworks that subclass to inject behavior.","solutions":["Prefer composition: wrap a ClientSession instance in your own class and delegate calls (`self._session.get(...)`).","Configure behavior via constructor args (`headers=`, `middlewares=`, `trace_configs=`) rather than overrides.","For per-request behavior use the `middlewares=` argument (client middleware chain) instead of method overrides."],"exampleFix":"// before\nclass MySession(aiohttp.ClientSession):\n    async def get(self, url):\n        log.info(url)\n        return await super().get(url)\n// after\nclass MyClient:\n    def __init__(self, session: aiohttp.ClientSession):\n        self._s = session\n    async def get(self, url):\n        log.info(url)\n        return await self._s.get(url)","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not subclass ClientSession; the prohibition is intentional and won't be lifted.","Use composition: wrap a ClientSession instance in your own class.","Configure headers, cookies, middlewares, and trace_configs through the constructor instead of overrides.","Add a lint rule (bandit/grep) rejecting `class \\w+\\(.*ClientSession\\)`."],"tags":["client","api-misuse","inheritance","design"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}