Textualize/textual · error · NoScreen

node has no screen

Error message

node has no screen

What it means

NoScreen is raised by the screen property when the node has no Screen ancestor: the widget is not mounted on a running app/screen, so no screen can be resolved by walking _parent links.

Source

Thrown at src/textual/dom.py:806

            A screen object.

        Raises:
            NoScreen: If this node isn't mounted (and has no screen).
        """
        # Get the node by looking up a chain of parents
        # Note that self.screen may not be the same as self.app.screen
        from textual.screen import Screen

        node: MessagePump | None = self
        try:
            while node is not None and not isinstance(node, Screen):
                node = node._parent
        except AttributeError:
            raise RuntimeError(
                "Widget is missing attributes; have you called the constructor in your widget class?"
            ) from None
        if not isinstance(node, Screen):
            raise NoScreen("node has no screen")
        return node

    @property
    def id(self) -> str | None:
        """The ID of this node, or None if the node has no ID."""
        return self._id

    @id.setter
    def id(self, new_id: str) -> str:
        """Sets the ID (may only be done once).

        Args:
            new_id: ID for this node.

        Raises:
            ValueError: If the ID has already been set.
        """
        check_identifiers("id", new_id)

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Move code that needs self.screen out of __init__ into on_mount or a lifecycle hook that runs after mounting
  2. In tests, use textual's run_test() async context / Pilot so widgets are mounted on a screen
  3. If the widget may be detached, check for a screen defensively before use

Example fix

# before
class MyWidget(Widget):
    def __init__(self):
        super().__init__()
        self.bg = self.screen.background  # NoScreen

# after
class MyWidget(Widget):
    def on_mount(self) -> None:
        self.bg = self.screen.background
Defensive patterns

Strategy: validation

Validate before calling

def safe_screen(widget):
    node = widget
    while node is not None and not isinstance(node, Screen):
        node = node._parent
    return node  # None if not mounted

scr = safe_screen(widget)
if scr is None:
    defer_work_until_mount()

Type guard

from textual.screen import Screen
def is_mounted(widget) -> bool:
    return widget.screen is not None if widget.is_mounted else False

Try / catch

from textual.screen import NoScreen
try:
    scr = widget.screen
except NoScreen:
    schedule_after_mount(widget)  # retry later

Prevention

When it happens

Trigger: Accessing widget.screen (or APIs that use it: screen.refresh, posting to screen, styles resolution) before the widget is mounted, e.g. inside __init__ or compose() before the widget is attached to a Screen.

Common situations: Calling self.screen in a widget constructor or in compose(); using widgets standalone in unit tests without an app runner; accessing a removed widget after remove() breaks the parent chain.

Related errors


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