Selenium NoSuchElementException (tested)
NoSuchElementException: no such element: Unable to locate element means find_element() found nothing in the current document at that moment. Check the selector in the message, wait for content added by JavaScript, and switch into iframes or shadow roots before searching.
Setup used in every block
common.py writes a small HTML page to a local file and starts headless Chrome. Exception messages are cut to their first line (Selenium appends the session info and a documentation link).
import os, pathlib
from selenium import webdriver
CHROME = os.path.expanduser("~/.cache/ms-playwright/chromium-1248/chrome-linux64/chrome")
def page(html):
p = pathlib.Path("/tmp/sl383/page.html"); p.write_text(html); return p.as_uri()
def driver():
o = webdriver.ChromeOptions()
o.binary_location = CHROME
o.add_argument("--headless=new"); o.add_argument("--no-sandbox"); o.add_argument("--window-size=1000,700")
return webdriver.Chrome(options=o)
1. Reproduce it: the locator matches nothing
from common import *
from selenium.webdriver.common.by import By
d = driver()
d.get(page('<input id="search-box" class="form-control big" name="q">'))
for by, value in [(By.ID, "search"), (By.ID, "#search-box"), (By.NAME, "q "), (By.XPATH, "//input[@id='search-box']")]:
try:
d.find_element(by, value)
print(f"{by}={value!r}: found")
except Exception as e:
print(f"{by}={value!r}: {type(e).__name__}: {e.msg.splitlines()[0]}")
print("find_elements on a miss:", d.find_elements(By.ID, "search"))
d.quit()
Output:
id='search': NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"[id="search"]"}
id='#search-box': NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"[id="#search-box"]"}
name='q ': NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"[name="q "]"}
xpath="//input[@id='search-box']": found
find_elements on a miss: []
The message shows the locator chromedriver actually used. By.ID and By.NAME are sent as CSS attribute selectors ([id="..."]) that must match the attribute exactly: a partial id, a leading # or a stray space finds nothing. Copy the value from the page source, or test it in the browser console with document.querySelectorAll('[id="search"]'). If you only want to check whether an element exists, find_elements() returns an empty list instead of raising.
2. The element is added later by JavaScript
from common import *
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
html = '''<div id="out"></div>
<script>setTimeout(() => document.getElementById("out").innerHTML = "<p id=result>42</p>", 1000)</script>'''
d = driver()
d.get(page(html))
try:
d.find_element(By.ID, "result")
except Exception as e:
print("immediately:", type(e).__name__)
print("explicit wait:", WebDriverWait(d, 5).until(EC.presence_of_element_located((By.ID, "result"))).text)
d.get(page(html))
d.implicitly_wait(5) # applies to every find_element call from now on
print("implicit wait:", d.find_element(By.ID, "result").text)
d.quit()
Output:
immediately: NoSuchElementException
explicit wait: 42
implicit wait: 42
find_element() looks once, immediately. For content rendered after load, wait with WebDriverWait(...).until(EC.presence_of_element_located(...)). implicitly_wait(5) also works but applies to every lookup for the rest of the session, including the ones you expect to fail, which then each take 5 seconds; the Selenium docs advise against mixing it with explicit waits.
3. The element is inside an iframe or a shadow root
from common import *
from selenium.webdriver.common.by import By
d = driver()
d.get(page('''<iframe srcdoc="<input id=card>"></iframe>
<my-widget></my-widget>
<script>document.querySelector("my-widget").attachShadow({mode: "open"}).innerHTML = "<button id=buy>Buy</button>"</script>'''))
for id_ in ("card", "buy"):
try:
d.find_element(By.ID, id_)
except Exception as e:
print(f"#{id_}:", type(e).__name__)
d.switch_to.frame(d.find_element(By.TAG_NAME, "iframe"))
print("#card after switch_to.frame:", d.find_element(By.ID, "card").tag_name)
d.switch_to.default_content()
root = d.find_element(By.TAG_NAME, "my-widget").shadow_root
print("#buy via shadow_root:", root.find_element(By.CSS_SELECTOR, "#buy").text)
try:
root.find_element(By.XPATH, ".//button")
except Exception as e:
print("XPath inside shadow_root:", type(e).__name__)
d.quit()
Output:
#card: NoSuchElementException
#buy: NoSuchElementException
#card after switch_to.frame: input
#buy via shadow_root: Buy
XPath inside shadow_root: InvalidArgumentException
Neither element is found from the main document, although both are visible on the page. For an <iframe> (payment forms, embedded widgets), switch into it with driver.switch_to.frame(...) and back with switch_to.default_content(). For a web component with a shadow root, get the host element's .shadow_root and search inside it with a CSS selector: XPath is not supported there (InvalidArgumentException).
4. Checklist
- Read the selector in the error message and test it in DevTools on the same page.
- Is the page the one you think? Print
driver.current_urlanddriver.title: a login redirect or a consent page is a common surprise. - Dynamic content:
WebDriverWait+presence_of_element_located. - iframe:
switch_to.frame. Shadow DOM:.shadow_root+ CSS. - Generated ids (
ember123,react-select-5-input) change between runs: use a stable attribute instead.
Related: Selenium TimeoutException: Message:, Selenium element not interactable, Selenium StaleElementReferenceException.
Frequently asked questions
What causes NoSuchElementException in Selenium?
The locator matches nothing in the current document right now: a wrong or partial selector, content that JavaScript adds later, or an element inside an iframe or shadow root.
How do I check if an element exists without an exception?
Use driver.find_elements(by, value): it returns an empty list when nothing matches instead of raising NoSuchElementException.
Written with AI assistance. Measured on 2026-10-23 with Selenium 4.51.0 (Python 3.13.5) and headless Chrome for Testing 156.0.8078.4. Each block was run verbatim and the output pasted unedited. PlainHeaders is our extension; Python, Selenium and Chrome are free.