Selenium ElementNotInteractableException (tested)

Selenium raises ElementNotInteractableException: element not interactable when send_keys() or click() targets an element that is in the DOM but hidden, disabled, or not an input. Most often your locator matched a hidden duplicate first, or the element has not appeared yet: wait with EC.element_to_be_clickable.

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 element is hidden

from common import *
from selenium.webdriver.common.by import By
d = driver()
d.get(page('<input id="q" style="display:none"><button id="go" hidden>Go</button>'))
for action in ("send_keys", "click"):
    el = d.find_element(By.ID, "q" if action == "send_keys" else "go")
    print(action, "is_displayed:", el.is_displayed())
    try:
        el.send_keys("hello") if action == "send_keys" else el.click()
    except Exception as e:
        print(" ", type(e).__name__ + ":", e.msg.splitlines()[0])
d.quit()

Output:

send_keys is_displayed: False
  ElementNotInteractableException: element not interactable
click is_displayed: False
  ElementNotInteractableException: element not interactable

Selenium finds elements that are in the DOM whether or not they are visible, so find_element() succeeds. The error comes on send_keys() or click(), because a display:none or hidden element cannot be typed into or clicked. is_displayed() returns False for it.

2. Your locator matched a hidden duplicate first

from common import *
from selenium.webdriver.common.by import By
d = driver()
d.get(page('''
<div class="mobile" style="display:none"><input name="email"></div>
<div class="desktop"><input name="email"></div>'''))
els = d.find_elements(By.NAME, "email")
print("matches:", len(els), [e.is_displayed() for e in els])
try:
    d.find_element(By.NAME, "email").send_keys("a@b.c")
except Exception as e:
    print(type(e).__name__ + ":", e.msg.splitlines()[0])
visible = next(e for e in els if e.is_displayed())
visible.send_keys("a@b.c")
print("visible one:", visible.get_attribute("value"))
d.find_element(By.CSS_SELECTOR, ".desktop input[name=email]").clear()
print("scoped selector OK")
d.quit()

Output:

matches: 2 [False, True]
ElementNotInteractableException: element not interactable
visible one: a@b.c
scoped selector OK

This is the most common cause on real sites: a responsive page has two copies of a form (mobile and desktop) or a hidden template, and find_element() returns the first match in document order, which is the hidden one. Check len(driver.find_elements(...)). If there is more than one match, pick the visible one or scope the selector to the visible container.

3. The element is not visible yet: wait for it

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 = '''<input id="q" style="display:none">
<script>setTimeout(() => document.getElementById("q").style.display = "", 1500)</script>'''
d = driver()
d.get(page(html))
try:
    d.find_element(By.ID, "q").send_keys("too early")
except Exception as e:
    print("immediately:", type(e).__name__ + ":", e.msg.splitlines()[0])
d.get(page(html))
el = WebDriverWait(d, 5).until(EC.element_to_be_clickable((By.ID, "q")))
el.send_keys("after wait")
print("after WebDriverWait:", el.get_attribute("value"))
d.quit()

Output:

immediately: ElementNotInteractableException: element not interactable
after WebDriverWait: after wait

If the page reveals the element with JavaScript (after an animation, an AJAX call, or a click that opens a modal), acting immediately fails. WebDriverWait(driver, 5).until(EC.element_to_be_clickable(locator)) polls until the element is displayed and enabled, then returns it. time.sleep() also works but is slower and still flaky.

4. What exactly counts as "not interactable" (measured)

from common import *
from selenium.webdriver.common.by import By
d = driver()
d.get(page('''<div id="plain">text</div>
<input id="zero" style="width:0;height:0;border:0;padding:0">
<input id="off" style="position:absolute;left:-9999px">
<input id="dis" disabled>
<input id="ro" readonly>
<input id="hid" style="visibility:hidden">
<input id="op" style="opacity:0">'''))
for id_ in ["plain", "zero", "off", "dis", "ro", "hid", "op"]:
    el = d.find_element(By.ID, id_)
    try:
        el.send_keys("x")
        r = "OK value=%r" % el.get_attribute("value")
    except Exception as e:
        r = type(e).__name__ + ": " + e.msg.splitlines()[0]
    print(f"{id_:5} displayed={el.is_displayed()!s:5} enabled={el.is_enabled()!s:5} -> {r}")
d.quit()

Output:

plain displayed=True  enabled=True  -> ElementNotInteractableException: element not interactable
zero  displayed=False enabled=True  -> OK value='x'
off   displayed=False enabled=True  -> OK value='x'
dis   displayed=True  enabled=False -> ElementNotInteractableException: element not interactable
ro    displayed=True  enabled=True  -> OK value=''
hid   displayed=False enabled=True  -> ElementNotInteractableException: element not interactable
op    displayed=False enabled=True  -> OK value='x'

Chrome raises the error for send_keys() into a plain <div> (it is not an input or contenteditable, so send keys to the input inside it instead), for disabled inputs and for visibility:hidden. It does not raise for a zero-size, off-screen or opacity:0 input, even though is_displayed() is False for those: the text is typed. A readonly input raises nothing either, but the value stays empty, so check get_attribute("value") after typing.

5. Checklist

Frequently asked questions

What causes element not interactable in Selenium?

The element exists but cannot receive input: it is display:none or visibility:hidden, disabled, not yet shown by JavaScript, or not an input at all (e.g. a div). Often the locator matched a hidden duplicate first.

How do I fix ElementNotInteractableException?

Check how many elements your locator matches and pick the visible one, wait with WebDriverWait and EC.element_to_be_clickable, and send keys to the actual input element.

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.