{"id":"7c24bf80a055006a","repo":"aio-libs/aiohttp","slug":"module-name-has-no-attribute-name","errorCode":null,"errorMessage":"module {__name__} has no attribute {name}","messagePattern":"module (.+?) has no attribute (.+?)","errorType":"exception","errorClass":"AttributeError","httpStatus":null,"severity":"error","filePath":"aiohttp/__init__.py","lineNumber":258,"sourceCode":"def __dir__() -> tuple[str, ...]:\n    return __all__ + (\"__doc__\",)\n\n\ndef __getattr__(name: str) -> object:\n    global GunicornUVLoopWebWorker, GunicornWebWorker\n\n    # Importing gunicorn takes a long time (>100ms), so only import if actually needed.\n    if name in (\"GunicornUVLoopWebWorker\", \"GunicornWebWorker\"):\n        try:\n            from .worker import GunicornUVLoopWebWorker as guv, GunicornWebWorker as gw\n        except ImportError:\n            return None\n\n        GunicornUVLoopWebWorker = guv  # type: ignore[misc]\n        GunicornWebWorker = gw  # type: ignore[misc]\n        return guv if name == \"GunicornUVLoopWebWorker\" else gw\n\n    raise AttributeError(f\"module {__name__} has no attribute {name}\")\n","sourceCodeStart":240,"sourceCodeEnd":259,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/__init__.py#L240-L259","documentation":"Raised by the module-level __getattr__ in aiohttp/__init__.py when code accesses an attribute that is not part of the public API (__all__) and is not one of the lazily-imported gunicorn workers (GunicornUVLoopWebWorker, GunicornWebWorker). It is aiohttp's PEP 562 lazy-import fallback for unknown names. It almost always means a typo, a removed/renamed symbol, or code written against a different aiohttp version.","triggerScenarios":"Any expression of the form aiohttp.<name> where <name> is not exported in aiohttp.__all__ and is not 'GunicornUVLoopWebWorker'/'GunicornWebWorker'. Common triggers: accessing a name that existed in aiohttp 3.x but was removed/moved in 4.x (e.g. relocated websocket helpers, renamed exceptions), or a typo like aiohttp.ClinetSession.","commonSituations":"Upgrading aiohttp across a major version (3.x -> 4.x) where symbols were reorganized; copy-pasting examples that import from the wrong path; relying on private helpers that live under aiohttp._websocket or aiohttp.http_websocket instead of the top-level package; static-analysis/IDE auto-complete picking a non-exported name.","solutions":["Check aiohttp.__all__ (or dir(aiohttp)) for the exact public name you intended and fix the typo/rename.","Import submodules directly from their real location, e.g. 'from aiohttp.http_websocket import WebSocketWriter' or 'from aiohttp._websocket.models import WSCloseCode', rather than reaching through the top-level package.","If upgrading, consult the CHANGES/ folder and migration notes for renamed/removed symbols and update call sites.","If you need the lazily-loaded gunicorn workers, ensure gunicorn is installed (the __getattr__ returns None on ImportError for those two names, so a different error appears)."],"exampleFix":"# before\nimport aiohttp\nworker = aiohttp.GunicornSteamWorker  # typo -> AttributeError\n\n# after\nimport aiohttp\nworker = aiohttp.GunicornWebWorker  # correct lazily-loaded name","handlingStrategy":"validation","validationCode":"import aiohttp\nname = 'GunicornWebWorker'\nif name not in aiohttp.__all__ and name not in {'GunicornUVLoopWebWorker', 'GunicornWebWorker'}:\n    raise AttributeError(f'{name!r} is not a public aiohttp attribute')\nobj = getattr(aiohttp, name)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Import submodules from their real path instead of reaching through the top-level package.","When upgrading aiohttp, grep your code for 'aiohttp.' usages and reconcile against aiohttp.__all__ and the CHANGES/ notes.","Let your IDE/linter resolve names against the installed package so typos surface at edit time."],"tags":["api","import","attributes","version-migration"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}