2025, Oct 16 17:00

Selenium Relative Locators: Fix the 'RelativeBy not JSON serializable' Error with driver.find_element

Troubleshoot Selenium 4 Relative Locators in Python: avoid TypeError 'RelativeBy not JSON serializable' by using driver find_element with a WebElement anchor.

Running into a TypeError when working with Selenium relative locators can be confusing, especially when the method signature suggests it should work. If you see an exception about RelativeBy not being JSON serializable while calling find_element on a WebElement, this guide shows exactly why it happens and how to fix it without changing your intended selector logic.

Reproducing the issue

The example below is simplified to demonstrate the problem. The environment uses Selenium 4.33.0 with Python 3.13.6. The code tries to use a Relative Locator with element-level find_element, which triggers the error.

import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.relative_locator import locate_with
site_url = "https://www.lazyvim.org/"
browser = webdriver.Chrome(webdriver.ChromeOptions())
browser.maximize_window()
browser.get(site_url)
# pause just to observe the page during the demo
time.sleep(3)
# implicit wait only for the sake of the demo
browser.implicitly_wait(3)
# left sidebar
side_css = (By.CSS_SELECTOR, "div.sidebar_njMd")
side_panel = browser.find_element(*side_css)
# build a RelativeBy locator for the caret button to the right of a link
caret_rel = locate_with(
    By.CSS_SELECTOR, "button.clean-btn.menu__caret"
).straight_right_of({By.CSS_SELECTOR: 'a[href="/configuration"]'})
# problematic call: passing RelativeBy to element-level find_element
caret_button = side_panel.find_element(caret_rel)
# attempt to click the caret
caret_button.click()
time.sleep(4)
browser.quit()

The run fails with:

TypeError: Object of type RelativeBy is not JSON serializable

What’s actually going on

The root cause is in how Selenium routes locators through WebDriver commands. Relative Locators, created via locate_with and represented as RelativeBy, are supported at the driver level. In contrast, when you call find_element on a WebElement, Selenium expects a regular By locator, not a RelativeBy. Passing a RelativeBy to element.find_element forces Selenium to serialize an object it doesn’t expect in that context, which results in the JSON serialization error you’re seeing.

Put simply: Relative Locators work with the WebDriver instance, not with a WebElement instance. When you switch the call to driver.find_element with a RelativeBy, the error disappears, even if the underlying target on the page remains the same.

The fix

Keep using Relative Locators, but call find_element on the driver. You can still scope the relation by building the anchor element first and passing that WebElement into the relative chain.

import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.relative_locator import locate_with
site_url = "https://www.lazyvim.org/"
browser = webdriver.Chrome(webdriver.ChromeOptions())
browser.maximize_window()
browser.get(site_url)
# pause to observe behavior in the demo
time.sleep(3)
# implicit wait only for demo purposes
browser.implicitly_wait(3)
# left sidebar
side_panel = browser.find_element(By.CSS_SELECTOR, "div.sidebar_njMd")
# use driver-level Relative Locator; anchor is resolved as a WebElement
caret_button = browser.find_element(
    locate_with(By.CSS_SELECTOR, "button.clean-btn.menu__caret").straight_right_of(
        side_panel.find_element(By.CSS_SELECTOR, 'a[href="/configuration"]')
    )
)
# click the caret
caret_button.click()
time.sleep(4)

Why this matters

Understanding where Relative Locators are supported prevents hard-to-read failures and time lost on debugging serialization errors that don’t point directly to the misuse. It also keeps your tests stable when you mix traditional By selectors with relative strategies in the same flow. The difference between driver.find_element and element.find_element may look minor, but in this case it changes how Selenium packages the command it sends to the browser.

Takeaways

If you need to position a target element relative to another element, build the anchor as a WebElement, then pass it into a driver-level Relative Locator. Avoid passing RelativeBy into element.find_element; element-level calls should use regular By locators. If switching from an element-level to a driver-level call changes the outcome, double-check the anchors and scoping on the page, but keep the core rule in mind: Relative Locators belong to the driver context.

The article is based on a question from StackOverflow by B1LLP4RK and an answer by Ajeet Verma.