Textualize/textual · error · ValueError

No common ancestor found

Error message

No common ancestor found

What it means

get_commonancestor walks both widgets' ancestor chains looking for a shared node; if none exists (different trees) and no default was supplied, it raises ValueError.

Source

Thrown at src/textual/widget.py:718

            ValueError: If there is no common ancestor and `default` is not provided (will not occur if both widgets are attached to the same DOM).

        Args:
            widget1: A Widget.
            widget2: A second widgets.
            default: A widget to return if no common ancestor is found.

        Returns:
            A common ancestor widgets.
        """
        ancestors1 = widget1.ancestors
        ancestors2 = set(widget2.ancestors)
        for node in ancestors1:
            if node in ancestors2:
                assert isinstance(node, Widget)
                return node
        if default is not None:
            return default
        raise ValueError("No common ancestor found")

    def focus_on_click(self) -> bool:
        """Automatically focus the widget on click?

        Implement this if you want to change the default click to focus behavior.
        The default will return the classvar `FOCUS_ON_CLICK`.

        Returns:
            `True` if Textual should set focus automatically on a click, or `False` if it shouldn't.
        """
        return self.FOCUS_ON_CLICK

    def get_line_filters(self) -> Sequence[LineFilter]:
        """Get the line filters enabled for this widget.

        Returns:
            A sequence of [LineFilter][textual.filters.LineFilter] instances.
        """

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Pass a default: get_common_ancestor(other, default=app) or default=None
  2. Ensure both widgets are mounted under the same root before comparing
  3. Check 'widget.is_attached' on both first

Example fix

# before
ancestor = a.get_common_ancestor(b)
# after
ancestor = a.get_common_ancestor(b, default=None)
if ancestor is None:
    return
Defensive patterns

Strategy: fallback

Try / catch

try:
    anc = a.get_common_ancestor(b)
except ValueError:
    anc = None

Prevention

When it happens

Trigger: Calling widget.get_common_ancestor(other) where one widget is mounted and the other is not, or they live under different roots (e.g. two separate screens or an unmounted subtree).

Common situations: Comparing widgets during mount/unmount transitions, or widgets from a modal screen vs the base screen.

Related errors


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