Textualize/textual · error · NoMatches

No nodes match {self!r} on {self.node!r}

Error message

No nodes match {self!r} on {self.node!r}

What it means

first() raises NoMatches when the selector matched zero nodes on the queried DOM node. This is the canonical 'selector found nothing' error for query_one/first, including the query and node in the message for debugging.

Source

Thrown at src/textual/css/query.py:249

        Raises:
            WrongType: If the wrong type was found.
            NoMatches: If there are no matching nodes in the query.

        Returns:
            The matching Widget.
        """
        _rich_traceback_omit = True
        if self.nodes:
            first = self.nodes[0]
            if expect_type is not None:
                if not isinstance(first, expect_type):
                    raise WrongType(
                        f"Query value is the wrong type; expected type {expect_type.__name__!r}, found {first}"
                    )
            return first
        else:
            raise NoMatches(f"No nodes match {self!r} on {self.node!r}")

    if TYPE_CHECKING:

        @overload
        def only_one(self) -> QueryType: ...

        @overload
        def only_one(self, expect_type: type[ExpectType]) -> ExpectType: ...

    def only_one(
        self, expect_type: type[ExpectType] | None = None
    ) -> QueryType | ExpectType:
        """Get the *only* matching node.

        Args:
            expect_type: Require matched node is of this type,
                or None for any type.

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Verify the selector matches something in the current DOM (ids spelled the same, widget actually composed)
  2. Move the query to after mounting — e.g. on_mount of the container, or call_before adding with mount and use the returned widget
  3. Use query(...).first() guarded, or catch NoMatches and compose/mount the missing widget on demand

Example fix

# before
def on_mount(self) -> None:
    self.query_one("#late").focus()  # NoMatches
# after
def on_mount(self) -> None:
    self.call_after_refresh(self._focus_late)

def _focus_late(self) -> None:
    self.query_one("#late").focus()
Defensive patterns

Strategy: try-catch

Validate before calling

matches = screen.query(selector).nodes
if matches:
    node = matches[0]

Try / catch

from textual.css.query import NoMatches
try:
    w = self.query_one("#id")
except NoMatches:
    w = None

Prevention

When it happens

Trigger: `self.query_one("#submit")` before the widget is mounted or when the id is misspelled; querying a selector that exists only after compose() completes; querying a removed widget.

Common situations: Querying during on_mount before children mount (ordering), wrong/renamed ids, conditional widgets not yet created, or querying the wrong screen/node.

Related errors


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