Textualize/textual · error · TooManyMatches

Call to only_one resulted in more than one matched node

Error message

Call to only_one resulted in more than one matched node

What it means

only_one() asserts the query matched exactly one node: after accessing nodes[0] it probes nodes[1]; succeeding means multiple matches, raising TooManyMatches. It exists for strict single-target operations where acting on many nodes would be a bug.

Source

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

            TooManyMatches: If there is more than one matching node in the query.

        Returns:
            The matching Widget.
        """
        _rich_traceback_omit = True
        # Call on first to get the first item. Here we'll use all of the
        # testing and checking it provides.
        the_one: ExpectType | QueryType = (
            self.first(expect_type) if expect_type is not None else self.first()
        )
        try:
            # Now see if we can access a subsequent item in the nodes. There
            # should *not* be anything there, so we *should* get an
            # IndexError. We *could* have just checked the length of the
            # query, but the idea here is to do the check as cheaply as
            # possible. "There can be only one!" -- Kurgan et al.
            _ = self.nodes[1]
            raise TooManyMatches(
                "Call to only_one resulted in more than one matched node"
            )
        except IndexError:
            # The IndexError was got, that's a good thing in this case. So
            # we return what we found.
            pass
        return the_one

    if TYPE_CHECKING:

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

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

    def last(
        self, expect_type: type[ExpectType] | None = None

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Narrow the selector (add #id, scope to a container like `#sidebar Button`) so exactly one node matches
  2. If multiple matches are acceptable, use first()/last() or iterate instead of only_one()
  3. Ensure class_name manipulation (add_class/remove_class) keeps the selector unique

Example fix

# before
self.query(".selected").only_one()
# after
self.query("#sidebar .selected").only_one()  # or .first()
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

from textual.css.query import TooManyMatches
try:
    node = q.only_one()
except TooManyMatches:
    node = q.first()  # or narrow selector

Prevention

When it happens

Trigger: `self.query("Button").only_one()` when multiple Buttons exist; a selector intended to be unique (e.g. `.selected`) matching several widgets because the class was applied to many nodes.

Common situations: Assuming a class/id is unique when it's not; widgets duplicated across layers/screens; dynamic creation producing extra matches.

Related errors


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