Textualize/textual · error · SignalError

Node must be running to subscribe to a signal (has {node} be

Error message

Node must be running to subscribe to a signal (has {node} been mounted)?

What it means

Textual's Signal system only allows live, mounted nodes to subscribe because delivery requires an active message pump. Subscribing a widget before it is running raises SignalError.

Source

Thrown at src/textual/signal.py:78

        node: DOMNode,
        callback: SignalCallbackType[SignalT],
        immediate: bool = False,
    ) -> None:
        """Subscribe a node to this signal.

        When the signal is published, the callback will be invoked.

        Args:
            node: Node to subscribe.
            callback: A callback function which takes a single argument and returns anything (return type ignored).
            immediate: Invoke the callback immediately on publish if `True`, otherwise post it to the DOM node to be
                called once existing messages have been processed.

        Raises:
            SignalError: Raised when subscribing a non-mounted widget.
        """
        if not node.is_running:
            raise SignalError(
                f"Node must be running to subscribe to a signal (has {node} been mounted)?"
            )

        if immediate:

            def signal_callback(data: SignalT) -> None:
                """Invoke the callback immediately."""
                callback(data)

        else:

            def signal_callback(data: SignalT) -> None:
                """Post the callback to the node, to call at the next opertunity."""
                node.call_next(callback, data)

        callbacks = self._subscriptions.setdefault(node, [])
        callbacks.append(signal_callback)

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Move subscribe() calls into on_mount (or the _on_mount handler)
  2. If subscribing another node, verify node.is_running first
  3. Unsubscribe in on_unmount to avoid leaks

Example fix

# before
class MyWidget(Widget):
    def __init__(self):
        super().__init__()
        MY_SIGNAL.subscribe(self, self.on_signal)  # not mounted yet
# after
class MyWidget(Widget):
    def on_mount(self) -> None:
        MY_SIGNAL.subscribe(self, self.on_signal)
Defensive patterns

Strategy: validation

Validate before calling

if node.is_running:
    SIGNAL.subscribe(node, callback)

Try / catch

from textual.signal import SignalError
try:
    SIGNAL.subscribe(self, self.on_signal)
except SignalError:
    self.call_after_refresh(lambda: SIGNAL.subscribe(self, self.on_signal))

Prevention

When it happens

Trigger: Calling signal.subscribe(node) in __init__, on the class, or in a handler that runs before mount; also subscribing an already-unmounted node.

Common situations: Wiring subscriptions in a widget constructor instead of on_mount; subscribing widgets created but never mounted; subscribing after removal from the DOM.

Related errors


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