Textualize/textual · error · WrongType

Node matching {query_selector!r} is the wrong type; expected

Error message

Node matching {query_selector!r} is the wrong type; expected type {expect_type.__name__!r}, found {node}

What it means

query_one found a node matching an #id selector, but the node is not an instance of the expected type given via the expect_type argument/overload, so WrongType is raised instead of silently returning a mismatched object.

Source

Thrown at src/textual/dom.py:1500

        base_node = self._get_dom_base()

        if isinstance(selector, str):
            query_selector = selector
        else:
            query_selector = selector.__name__

        if is_id_selector(query_selector):
            cache_key = (base_node._nodes._updates, query_selector, expect_type)
            cached_result = base_node._query_one_cache.get(cache_key)
            if cached_result is not None:
                return cached_result
            if (
                node := walk_breadth_search_id(
                    base_node, query_selector[1:], with_root=False
                )
            ) is not None:
                if expect_type is not None and not isinstance(node, expect_type):
                    raise WrongType(
                        f"Node matching {query_selector!r} is the wrong type; expected type {expect_type.__name__!r}, found {node}"
                    )
                base_node._query_one_cache[cache_key] = node
                return node
            raise NoMatches(f"No nodes match {query_selector!r} on {base_node!r}")

        try:
            selector_set = parse_selectors(query_selector)
        except TokenError:
            raise InvalidQueryFormat(
                f"Unable to parse {query_selector!r} as a query; check for syntax errors"
            ) from None

        if all(selectors.is_simple for selectors in selector_set):
            cache_key = (base_node._nodes._updates, query_selector, expect_type)
            cached_result = base_node._query_one_cache.get(cache_key)
            if cached_result is not None:
                return cached_result

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Update the query's expected type to the actual widget class, or fix compose() so the widget with that id is the expected class
  2. Use query() with isinstance filtering if multiple types are acceptable
  3. Ensure ids are unique per screen so queries resolve predictably

Example fix

# before
self.query_one('#name', Input)  # WrongType: it's a Label

# after
self.query_one('#name', Label)
# or change compose() to mount an Input with id='name'
Defensive patterns

Strategy: type-guard

Validate before calling

node = walk_to_find(base, '#foo')
from textual.widget import Widget
if node is not None and not isinstance(node, ExpectedType):
    fix_selector_or_compose()

Type guard

def node_is(node, expect_type) -> bool:
    return isinstance(node, expect_type)

Try / catch

from textual.css.query import WrongType, NoMatches
try:
    w = self.query_one('#foo', Input)
except WrongType:
    w = self.query_one('#foo')  # untyped, handle dynamically
except NoMatches:
    w = None

Prevention

When it happens

Trigger: Calling query_one('#foo', ExpectedType) where the node with id 'foo' is a different widget class; using the typed overload query_one('#foo', Input) when #foo is a Label.

Common situations: Changing a widget's class in compose() without updating queries; querying by id that collides across screens; type narrowing via query_one(str, type) with a stale type after refactors.

Related errors


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