Textualize/textual · error · NoMatches
No nodes match {query_selector!r} on {base_node!r}
Error message
No nodes match {query_selector!r} on {base_node!r} What it means
NoMatches from query_one when an #id (fast-path) lookup found no node with that id anywhere under the base node. The message echoes the selector and base node for debugging.
Source
Thrown at src/textual/dom.py:1505
query_selector = selector.__name__
if is_id_selector(query_selector):
cache_key = (base_node._nodes._updates, query_selector, expect_type)
cached_result = base_node._query_one_cache.get(cache_key)
if cached_result is not None:
return cached_result
if (
node := walk_breadth_search_id(
base_node, query_selector[1:], with_root=False
)
) is not None:
if expect_type is not None and not isinstance(node, expect_type):
raise WrongType(
f"Node matching {query_selector!r} is the wrong type; expected type {expect_type.__name__!r}, found {node}"
)
base_node._query_one_cache[cache_key] = node
return node
raise NoMatches(f"No nodes match {query_selector!r} on {base_node!r}")
try:
selector_set = parse_selectors(query_selector)
except TokenError:
raise InvalidQueryFormat(
f"Unable to parse {query_selector!r} as a query; check for syntax errors"
) from None
if all(selectors.is_simple for selectors in selector_set):
cache_key = (base_node._nodes._updates, query_selector, expect_type)
cached_result = base_node._query_one_cache.get(cache_key)
if cached_result is not None:
return cached_result
else:
cache_key = None
for node in walk_breadth_first(base_node, with_root=False):
if not match(selector_set, node):View on GitHub (pinned to 06dbeef4bb)
Solutions
- Verify the widget exists at query time — move the query to on_mount of the screen/app or after compose completes
- Check spelling of the id and that it's set at construction (Widget(id='x'))
- Use query_one_optional-style checks or query(...) with a None guard when existence is uncertain
Example fix
# before
def on_mount(self):
self.query_one('#input').focus() # NoMatches: children not mounted yet
# after
class MyApp(App):
async def on_mount(self):
await self.push_screen(MainScreen())
class MainScreen(Screen):
def on_mount(self):
self.query_one('#input').focus() # after compose, works Defensive patterns
Strategy: try-catch
Type guard
def has_id(base, node_id: str) -> bool:
return base.query(f'#{node_id}').first() is not None Try / catch
from textual.css.query import NoMatches
try:
w = self.query_one('#foo')
except NoMatches:
w = None
# mount it or defer Prevention
- Query from screen on_mount, not widget __init__
- Verify ids set in compose() match queried ids
- Use optional/empty-query patterns when existence is uncertain
When it happens
Trigger: Calling query_one('#missing') before the widget is mounted/composed; querying on a subtree that doesn't contain the id; typos or ids set after the query runs.
Common situations: Querying in on_mount of a parent before children mount; querying the app when the widget lives on a different screen; id set dynamically after query timing (race with worker/compose).
Related errors
- No nodes match {self!r} on {self.node!r}
- No nodes match {self!r} on dom{self.node!r}
- Node matching {query_selector!r} is the wrong type; expected
- Call to query_one resulted in more than one matched node
- Unable to parse filter {filter!r} as query
AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27).
Data as JSON: /api/errors/1c0b19319119fc51.
Report an issue: GitHub.