Textualize/textual · error · NoMatches

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

Error message

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

What it means

last() raises NoMatches when the query returned an empty node list, mirroring first()'s empty check but for the last-matched node accessor. The message (with its quirky 'dom' wording) includes the query repr and node for diagnosis.

Source

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

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

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

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

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

    if TYPE_CHECKING:

        @overload
        def results(self) -> Iterator[QueryType]: ...

        @overload
        def results(self, filter_type: type[ExpectType]) -> Iterator[ExpectType]: ...

    def results(
        self, filter_type: type[ExpectType] | None = None
    ) -> Iterator[QueryType | ExpectType]:

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Confirm the target widget exists and is mounted under the queried node at call time
  2. Defer the query until after mount/compose (call_after_refresh)
  3. Catch NoMatches to handle 'not present yet' gracefully

Example fix

# before
row = self.query("DataTable .row--highlight").last()  # NoMatches
# after
from textual.css.query import NoMatches
try:
    row = self.query("DataTable .row--highlight").last()
except NoMatches:
    row = None
Defensive patterns

Strategy: try-catch

Validate before calling

q = screen.query(selector)
node = q.nodes[-1] if q.nodes else None

Try / catch

from textual.css.query import NoMatches
try:
    node = q.last()
except NoMatches:
    node = None

Prevention

When it happens

Trigger: `screen.query("DataTable").last()` with no DataTable mounted; selectors referencing widgets on other screens; querying after remove() of the only match.

Common situations: Same family as first()'s NoMatches: timing (not yet mounted), misspelled selectors, or querying the wrong node subtree.

Related errors


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