Textualize/textual · error · NoMatches

No nodes match on dom

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.

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.

Appendix: 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)