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 = NoneView on GitHub (pinned to 06dbeef4bb)
Solutions
- Narrow the selector (add #id, scope to a container like `#sidebar Button`) so exactly one node matches
- If multiple matches are acceptable, use first()/last() or iterate instead of only_one()
- 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
- Scope selectors with ids/containers to guarantee uniqueness before only_one()
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
- Call to query_one resulted in more than one matched node
- Unable to parse filter {filter!r} as query
- Query value is the wrong type; expected type {expect_type.__
- No nodes match {self!r} on {self.node!r}
- No nodes match {self!r} on dom{self.node!r}
AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27).
Data as JSON: /api/errors/4bf857f5ea964ac0.
Report an issue: GitHub.