{"record":{"id":"4bf857f5ea964ac0","repo":"Textualize/textual","slug":"call-to-only-one-resulted-in-more-than-one-matched","errorCode":null,"errorMessage":"Call to only_one resulted in more than one matched node","messagePattern":"Call to only_one resulted in more than one matched node","errorType":"validation","errorClass":"TooManyMatches","httpStatus":null,"severity":"error","filePath":"src/textual/css/query.py","lineNumber":289,"sourceCode":"            TooManyMatches: If there is more than one matching node in the query.\n\n        Returns:\n            The matching Widget.\n        \"\"\"\n        _rich_traceback_omit = True\n        # Call on first to get the first item. Here we'll use all of the\n        # testing and checking it provides.\n        the_one: ExpectType | QueryType = (\n            self.first(expect_type) if expect_type is not None else self.first()\n        )\n        try:\n            # Now see if we can access a subsequent item in the nodes. There\n            # should *not* be anything there, so we *should* get an\n            # IndexError. We *could* have just checked the length of the\n            # query, but the idea here is to do the check as cheaply as\n            # possible. \"There can be only one!\" -- Kurgan et al.\n            _ = self.nodes[1]\n            raise TooManyMatches(\n                \"Call to only_one resulted in more than one matched node\"\n            )\n        except IndexError:\n            # The IndexError was got, that's a good thing in this case. So\n            # we return what we found.\n            pass\n        return the_one\n\n    if TYPE_CHECKING:\n\n        @overload\n        def last(self) -> QueryType: ...\n\n        @overload\n        def last(self, expect_type: type[ExpectType]) -> ExpectType: ...\n\n    def last(\n        self, expect_type: type[ExpectType] | None = None","sourceCodeStart":271,"sourceCodeEnd":307,"githubUrl":"https://github.com/Textualize/textual/blob/06dbeef4bb70fb718236aa418ed658ef4667a126/src/textual/css/query.py#L271-L307","documentation":"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.","triggerScenarios":"`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.","commonSituations":"Assuming a class/id is unique when it's not; widgets duplicated across layers/screens; dynamic creation producing extra matches.","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"],"exampleFix":"# before\nself.query(\".selected\").only_one()\n# after\nself.query(\"#sidebar .selected\").only_one()  # or .first()","handlingStrategy":"validation","validationCode":"nodes = screen.query(selector).nodes\nif len(nodes) == 1:\n    node = nodes[0]","typeGuard":null,"tryCatchPattern":"from textual.css.query import TooManyMatches\ntry:\n    node = q.only_one()\nexcept TooManyMatches:\n    node = q.first()  # or narrow selector","preventionTips":["Scope selectors with ids/containers to guarantee uniqueness before only_one()"],"tags":["textual","query","too-many-matches"],"backgroundTag":"query-ambiguous-matches","analyzedSha":"06dbeef4bb70fb718236aa418ed658ef4667a126","analyzedAt":"2026-08-27T02:36:57.214Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}