SeleniumHQ/selenium · error · InvalidSelectorException
Compound class names are not allowed.
Error message
Compound class names are not allowed.
What it means
Raised inside `ShadowRoot.find_element` when `by == By.CLASS_NAME` and the value contains internal whitespace after stripping (e.g. 'foo bar'). The shadow-root API converts CLASS_NAME into a CSS selector (`.value`), but a multi-class string is not a valid single CSS class, so it rejects it preemptively. It is an InvalidSelectorException.
Source
Thrown at py/selenium/webdriver/remote/shadowroot.py:81
- By.CSS_SELECTOR: Locate by a CSS selector.
- By.CLASS_NAME: Locate by the `class` attribute.
- By.TAG_NAME: Locate by the tag name (e.g., "input", "button").
- By.LINK_TEXT: Locate a link element by its exact text.
- By.PARTIAL_LINK_TEXT: Locate a link element by partial text match.
value: The locator value to use with the specified `by` strategy.
Returns:
The first matching `WebElement` found on the page.
Example:
>>> element = driver.find_element(By.ID, "foo")
"""
if by == By.ID:
by = By.CSS_SELECTOR
value = f'[id="{value}"]'
elif by == By.CLASS_NAME:
if value and any(char.isspace() for char in value.strip()):
raise InvalidSelectorException("Compound class names are not allowed.")
by = By.CSS_SELECTOR
value = f".{value}"
elif by == By.NAME:
by = By.CSS_SELECTOR
value = f'[name="{value}"]'
return self._execute(Command.FIND_ELEMENT_FROM_SHADOW_ROOT, {"using": by, "value": value})["value"]
def find_elements(self, by: str | By = By.ID, value: str | None = None) -> list[WebElement]:
"""Find elements inside a shadow root given a By strategy and locator.
Args:
by: The locating strategy to use. Default is `By.ID`. Supported values include:
- By.ID: Locate by element ID.
- By.NAME: Locate by the `name` attribute.
- By.XPATH: Locate by an XPath expression.
- By.CSS_SELECTOR: Locate by a CSS selector.
- By.CLASS_NAME: Locate by the `class` attribute.View on GitHub (pinned to aa36b38e69)
Solutions
- Use By.CSS_SELECTOR with a compound selector: '.btn.primary'.
- If you only need one distinguishing class, pass that single class name to CLASS_NAME.
- Switch to By.XPATH if the multi-class match is complex.
Example fix
# before shadow_root.find_element(By.CLASS_NAME, 'btn primary') # after shadow_root.find_element(By.CSS_SELECTOR, '.btn.primary')
Defensive patterns
Strategy: validation
Validate before calling
if by == By.CLASS_NAME and value and any(c.isspace() for c in value.strip()):
raise InvalidSelectorException('use By.CSS_SELECTOR for compound classes, e.g. .a.b') Type guard
def is_single_class_name(value: str) -> bool:
return not (value and any(c.isspace() for c in value.strip())) Prevention
- Never paste a multi-class attribute into CLASS_NAME; build a CSS selector instead.
- Centralize locators so compound classes are caught in review.
When it happens
Trigger: Calling `shadow_root.find_element(By.CLASS_NAME, 'btn primary')` or any value with embedded spaces/tabs. Whitespace at the ends is stripped first, so only inner whitespace triggers it.
Common situations: Selecting elements that carry several classes by passing the full class attribute string; copying the className straight from HTML into the locator.
Related errors
- input must be a string
- Invalid locator
- Unexpected compiled output format. Expected it to start with
- Unexpected compiled output format. Expected it to start with
- Unexpected compiled output format. Expected it to start with
AI-assisted analysis of SeleniumHQ/selenium@aa36b38e69 (2026-08-14).
Data as JSON: /api/errors/038de26a4f2f2591.
Report an issue: GitHub.