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

  1. Verify the widget exists at query time — move the query to on_mount of the screen/app or after compose completes
  2. Check spelling of the id and that it's set at construction (Widget(id='x'))
  3. 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

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


AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27). Data as JSON: /api/errors/1c0b19319119fc51. Report an issue: GitHub.