ErrLookup › Background articles › AttributeError: module 'X' has no attribute 'Y' — what Python's missing-attribute error means across libraries

AttributeError: module 'X' has no attribute 'Y' — what Python's missing-attribute error means across libraries

AttributeError: module 'x' has no attribute 'y' is the error Python raises when attribute lookup fails, and developers meet it most often importing from lazily-loaded packages (selenium.webdriver, vllm, litellm, scrapling, headroom.providers) where the name is misspelled, lives only in a submodule, or was renamed or removed in another release. The same exception class also enforces API rules — class-only pydantic metadata, read-only Selenium objects, unknown config fields — and can even wrap a missing dependency. Working out which of these you hit is the first step to the fix.

Distilled from 193 documented records across 45 repositories.

Background

AttributeError is the interpreter's native signal that an attribute lookup failed. There is no separate exception type for modules, classes, or instances, so libraries of every kind funnel very different problems into this one class. In the records collected here, the single largest producer is PEP 562 module-level __getattr__: packages such as selenium.webdriver, vllm, litellm, scrapling, poetry.utils.helpers, headroom.providers and SQLAlchemy's private sqlalchemy.orm.evaluator keep explicit lazy-import maps so that heavy submodules load on demand; only names registered in the map resolve on access, and every other name falls through to the standard "module ... has no attribute ..." message. From the caller's side this is indistinguishable from a genuine typo, which is also the most common cause.

A second group of records uses the same exception as a migration and deprecation tool. pydantic_core.core_schema, CPython's ast module, and poetry.utils.helpers keep small tables of deprecated names that emit a warning and redirect to a replacement, while names outside the table fail cleanly with AttributeError. Other libraries raise it deliberately to retire an API: crawl4ai raises it when the removed markdown_v2 attribute is touched or when legacy kwargs such as provider or api_token reach generate_schema(), and SQLAlchemy 2.0 renamed EvaluatorCompiler to _EvaluatorCompiler so old imports miss. In these records the same message text can mean "misspelled", "moved", or "deliberately removed"; a deprecation hint, when present, is the fastest way to tell them apart.

A third group enforces access rules through descriptors and validators rather than lookup failure. Pydantic's LazyClassAttribute raises when schema metadata such as model_fields is read on an instance instead of the class; Selenium's FedCM Account objects are read-only snapshots whose descriptor __set__ always throws; headroom's harness config validates assigned field names and rejects rebinding of its nested config objects; psycopg2's Identifier.string refuses multi-part identifiers (the plural .strings always works); and pandas' Arrow duration accessors raise AttributeError — rather than ValueError — precisely so the .dt dispatcher reports "not supported for this dtype". SQLAlchemy raises it when a concrete-inheritance subclass touches a relationship that was not redeclared on it, and when an association proxy's value_attr cannot be resolved to a directly mapped attribute. A couple of records even reuse the message wording as a printed fail-fast label for pipeline guards, which shows how recognizable the phrasing has become.

Finally, the exception can mask a different root cause. Langflow's generated lazy packages catch ModuleNotFoundError and ImportError from deferred imports and re-raise them as AttributeError("Could not import ... from ..."), so a missing transitive dependency surfaces as an attribute failure — the real error sits in the chained __cause__. Pydantic's migration shim raises a plain AttributeError for '__path__' as a structural guard so the import machinery treats submodules as non-packages; tooling that probes this attribute sees an error that is by design, not a bug. Message wording and strictness are library-specific: some raise the standard f-string naming the module and attribute, others name the exact field, embed a migration hint, or chain the original exception.

Common causes

What usually fixes it

Documented occurrences

…and 173 more across the corpus — use search.

Honest provenance: generated on 2026-08-15 from AI-assisted analysis of the linked records. See how records are made.