SeleniumHQ/selenium · error · AttributeError

module {__name__!r} has no attribute {name!r}

Error message

module {__name__!r} has no attribute {name!r}

What it means

Selenium uses lazy submodule loading for `selenium.webdriver.chrome` via a module-level `__getattr__`. When you access an attribute on the package, it is only resolved if it is one of the registered lazy submodules (options, remote_connection, service, webdriver). Accessing any other name triggers Python's default missing-attribute behavior through this AttributeError. This is the standard mechanism Python uses to defer expensive imports and to report that a requested name simply does not exist on the package.

Source

Thrown at py/selenium/webdriver/chrome/__init__.py:28

#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

import importlib

_LAZY_SUBMODULES = ["options", "remote_connection", "service", "webdriver"]


def __getattr__(name):
    if name in _LAZY_SUBMODULES:
        module = importlib.import_module(f".{name}", __name__)
        globals()[name] = module
        return module
    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")


def __dir__():
    return sorted(_LAZY_SUBMODULES)

View on GitHub (pinned to aa36b38e69)

Solutions

  1. Import the class directly from its submodule: `from selenium.webdriver.chrome.options import Options`.
  2. If you only need the WebDriver: use `from selenium.webdriver import Chrome` (re-exported at the top level).
  3. Check the spelling against the four valid submodules: options, remote_connection, service, webdriver.
  4. Run `dir(selenium.webdriver.chrome)` to see the valid lazy names.

Example fix

// before
import selenium.webdriver.chrome as chrome
class Opts(chrome.Options):  # AttributeError
...
// after
from selenium.webdriver.chrome.options import Options
class Opts(Options):
    ...
Defensive patterns

Strategy: validation

Validate before calling

import selenium.webdriver.chrome as chrome
_VALID = {"options", "remote_connection", "service", "webdriver"}
name = "options"
assert name in _VALID, f"not a lazy submodule of selenium.webdriver.chrome: {name}
options = importlib.import_module(f"selenium.webdriver.chrome.{name}")

Type guard

def is_lazy_chrome_submodule(name: str) -> bool:
    return name in {"options", "remote_connection", "service", "webdriver"}

Try / catch

try:
    from selenium.webdriver.chrome.options import Options
except AttributeError as e:
    raise ImportError(f"do not access submodules via the package; import directly: {e}") from e

Prevention

When it happens

Trigger: Accessing an attribute on `selenium.webdriver.chrome` that is not in the `_LAZY_SUBMODULES` list, e.g. `selenium.webdriver.chrome.Options` (capital O), `selenium.webdriver.chrome.By`, or a misspelled name like `selenium.webdriver.chrome.webrdiver`. Also triggered by `getattr(selenium.webdriver.chrome, "nonexistent")`.

Common situations: Importing the class wrong: expecting `from selenium import webdriver; webdriver.chrome.Options` to work when it should be `webdriver.ChromeOptions` or `from selenium.webdriver.chrome.options import Options`. Typos after IDE auto-complete. Code written against an older/newer Selenium version where a submodule was renamed or moved. Attempting to reach a remote-only helper from the package root.

Related errors


AI-assisted analysis of SeleniumHQ/selenium@aa36b38e69 (2026-08-14). Data as JSON: /api/errors/ed91f21e62d685ab. Report an issue: GitHub.