Selenium Python: find_element_by_class_name() Stopped Working in v2.21 – Fixing 'Compound Class Name Not Permitted' Error

Selenium is a powerful tool for automating web browsers, widely used for testing web applications and scraping data. Python, with its simplicity and readability, is a popular choice for writing Selenium scripts. However, like any tool, Selenium evolves, and updates can sometimes break existing code.

One common issue users faced after upgrading to Selenium v2.21 was the sudden failure of find_element_by_class_name(), accompanied by the error message: "Compound class names not permitted". If you’ve encountered this error, you’re not alone. This blog post dives deep into why this error occurs, how to identify it, and step-by-step solutions to fix it. We’ll also cover best practices to avoid similar issues in the future.

Table of Contents#

  1. Understanding the 'Compound Class Name Not Permitted' Error
  2. Why Did This Happen in Selenium v2.21?
  3. How to Identify Compound Class Names
  4. Fixing the Error: Step-by-Step Solutions
  5. Alternative Locator Methods
  6. Best Practices to Avoid Future Issues
  7. Conclusion
  8. References

1. Understanding the 'Compound Class Name Not Permitted' Error#

The error "Compound class names not permitted" occurs when you pass a compound class name (i.e., a class name with spaces) to find_element_by_class_name().

What is a Compound Class Name?#

In HTML, the class attribute can contain multiple class names separated by spaces. For example:

<button class="btn primary large">Click Me</button>  

Here, btn, primary, and large are three distinct classes applied to the button. Together, they form a compound class name: "btn primary large".

Why Selenium Throws This Error#

Selenium’s find_element_by_class_name() method is designed to locate elements by a single class name (not multiple). Prior to v2.21, Selenium was more lenient: if you passed a compound class name (with spaces), it would silently use the first class name in the string (e.g., "btn" from "btn primary large"). However, this behavior was ambiguous and error-prone.

In Selenium v2.21, the Selenium team introduced a validation check to explicitly block compound class names. This was done to prevent accidental misuse and enforce clearer locator practices. Now, if you pass a class name with spaces, Selenium immediately throws the "Compound class names not permitted" error.

2. Why Did This Happen in Selenium v2.21?#

The change was introduced in Selenium v2.21.0 (released in 2012) as part of an effort to standardize locator behavior. The key motivation was to:

  • Eliminate ambiguity: Prior to v2.21, passing "btn primary" would use "btn" as the class name, which was not obvious to users.
  • Improve reliability: Relying on the first class in a compound name often led to flaky tests if the class order changed in the HTML.
  • Align with W3C standards: The W3C WebDriver specification explicitly states that the class name locator must match a single class.

Thus, the error was added to enforce correct usage of find_element_by_class_name().

3. How to Identify Compound Class Names#

To fix the error, you first need to confirm if the class name you’re using is compound. Here’s how:

Step 1: Inspect the Element#

Right-click the element in your browser and select "Inspect" (or press F12). In the DevTools, check the class attribute of the element.

Example of Compound vs. Single Class Names#

ScenarioHTML Code ExampleClass Type
Single class name<div class="header">...</div>Single (no spaces)
Compound class name<div class="header main active">...</div>Compound (spaces)

If the class attribute contains spaces, you’re dealing with a compound class name, and find_element_by_class_name() will fail.

4. Fixing the Error: Step-by-Step Solutions#

Let’s resolve the error with three practical solutions. We’ll use the following HTML snippet as an example throughout:

<!-- Target element with compound class: "btn primary large" -->  
<button class="btn primary large">Submit</button>  

Solution 1: Use a Single Class Name (Simplest Fix)#

If one of the classes in the compound name is unique to the element, use that single class instead of the full compound name.

Example Code:#

from selenium import webdriver  
 
driver = webdriver.Chrome()  
driver.get("https://example.com")  
 
# ❌ Old (broken) code: Compound class name  
# element = driver.find_element_by_class_name("btn primary large")  # Throws error  
 
# ✅ Fixed code: Use a single unique class (e.g., "primary" if unique)  
element = driver.find_element_by_class_name("primary")  
print(element.text)  # Output: "Submit"  

When to Use This:#

  • If one of the classes (e.g., "primary") uniquely identifies the element.
  • Avoid if the class is reused elsewhere (e.g., multiple buttons with class "btn").

Solution 2: Use CSS Selectors for Multiple Classes#

CSS selectors are flexible and allow targeting elements with multiple classes. To match an element with classes "btn", "primary", and "large", use the syntax .class1.class2.class3 (no spaces between classes).

Example Code:#

from selenium import webdriver  
 
driver = webdriver.Chrome()  
driver.get("https://example.com")  
 
# ✅ Use CSS selector to target elements with ALL specified classes  
element = driver.find_element_by_css_selector(".btn.primary.large")  
print(element.text)  # Output: "Submit"  

How It Works:#

  • .btn.primary.large tells the browser to find elements with all three classes (btn, primary, and large).
  • Spaces in CSS selectors denote parent-child relationships (e.g., .btn .primary would target a child element of .btn with class primary). Omit spaces to target the same element with multiple classes.

When to Use This:#

  • When the element requires multiple classes to be uniquely identified.
  • CSS selectors are fast and widely supported, making this the preferred solution for compound classes.

Solution 3: Use XPath for Flexible Class Matching#

XPath is another powerful locator strategy. You can use XPath to match elements with one or more classes in the compound name.

Option A: Exact Class Match (Brittle)#

Match the exact compound class string (not recommended for dynamic UIs, as class order or additions will break the locator):

# ❗ Brittle: Fails if classes are reordered (e.g., "primary btn large")  
element = driver.find_element_by_xpath("//button[@class='btn primary large']")  

Option B: Partial Class Match (More Robust)#

Use contains() to check for the presence of individual classes (safer for dynamic UIs):

# ✅ Robust: Matches elements with ALL specified classes (order doesn't matter)  
element = driver.find_element_by_xpath(  
    "//button[contains(@class, 'btn') and contains(@class, 'primary') and contains(@class, 'large')]"  
)  
print(element.text)  # Output: "Submit"  

When to Use This:#

  • When CSS selectors aren’t sufficient (e.g., complex hierarchical matches).
  • Use contains() with caution: It may match unintended elements (e.g., contains(@class, 'btn') also matches "btn-danger").

5. Alternative Methods to Locate Elements#

If class names are unreliable (e.g., dynamic or frequently changing), consider these alternative locators:

Locator MethodUse CaseExample Code
find_element_by_id()Element has a unique id attribute.driver.find_element_by_id("submit-btn")
find_element_by_name()Element has a unique name attribute.driver.find_element_by_name("email")
find_element_by_link_text()For links with exact visible text.driver.find_element_by_link_text("Login")
find_element_by_partial_link_text()For links with partial text.driver.find_element_by_partial_link_text("Log")

6. Best Practices to Avoid Future Issues#

To prevent the "Compound class name" error and write maintainable tests:

  1. Prefer Unique Locators: Use id or name attributes if available (they’re the most stable).
  2. Avoid Overly Specific Class Names: Compound classes (e.g., "btn primary large") are brittle if classes are added/removed.
  3. Use CSS Selectors for Classes: For multiple classes, CSS selectors (.class1.class2) are faster and cleaner than XPath.
  4. Test Locators for Uniqueness: Use browser DevTools to verify locators:
    • In Chrome DevTools, press Ctrl+F (or Cmd+F) in the Elements tab and paste your CSS selector/XPath to check matches.
  5. Upgrade Selenium: Modern Selenium versions (4.x+) deprecate find_element_by_* methods in favor of find_element(By.CLASS_NAME, ...). For example:
    from selenium.webdriver.common.by import By  
     
    # Modern syntax (Selenium 4.x+)  
    element = driver.find_element(By.CLASS_NAME, "primary")  # Single class  
    element = driver.find_element(By.CSS_SELECTOR, ".btn.primary")  # Multiple classes  

7. Conclusion#

The "Compound class names not permitted" error in Selenium v2.21+ is a guardrail to enforce better locator practices. By avoiding compound class names in find_element_by_class_name(), and instead using single classes, CSS selectors, or XPath, you can resolve the error and write more reliable automation scripts.

Remember: The best locator is one that is unique, stable, and easy to maintain. Prioritize CSS selectors for multiple classes, and always test locators in browser DevTools before adding them to your code.

8. References#