Textualize/textual · error · TypeError

A Reactive class is required; for example: MyApp.theme

Error message

A Reactive class is required; for example: MyApp.theme

What it means

DOMNode.set_reactive raises TypeError when the first argument is not a Reactive descriptor instance — e.g. passing a plain value, string, or the class instead of the reactive attribute object.

Source

Thrown at src/textual/dom.py:268

        self, reactive: Reactive[ReactiveType], value: ReactiveType
    ) -> None:
        """Sets a reactive value *without* invoking validators or watchers.

        Example:
            ```python
            self.set_reactive(App.theme, "textual-light")
            ```

        Args:
            reactive: A reactive property (use the class scope syntax, i.e. `MyClass.my_reactive`).
            value: New value of reactive.

        Raises:
            AttributeError: If the first argument is not a reactive.
        """
        name = reactive.name
        if not isinstance(reactive, Reactive):
            raise TypeError("A Reactive class is required; for example: MyApp.theme")
        if name not in self._reactives:
            raise AttributeError(
                f"No reactive called {name!r}; Have you called super().__init__(...) in the {self.__class__.__name__} constructor?"
            )
        setattr(self, f"_reactive_{name}", value)

    def mutate_reactive(self, reactive: Reactive[ReactiveType]) -> None:
        """Force an update to a mutable reactive.

        Example:
            ```python
            self.reactive_name_list.append("Jessica")
            self.mutate_reactive(MyClass.reactive_name_list)
            ```

        Textual will automatically detect when a reactive is set to a new value, but it is unable
        to detect if a value is _mutated_ (such as updating a list, dict, or attribute of an object).
        If you do wish to use a collection or other mutable object in a reactive, then you can call

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Pass the reactive descriptor itself: self.set_reactive(MyApp.theme, 'dark')
  2. Access it from the class that declares it, not an instance copy
  3. For normal updates, just assign: self.theme = 'dark'

Example fix

# before
self.set_reactive('theme', 'dark')
# after
self.set_reactive(MyApp.theme, 'dark')
# or simply
self.theme = 'dark'
Defensive patterns

Strategy: type-guard

Validate before calling

from textual.reactive import Reactive
from textual.app import App
assert isinstance(App.theme, Reactive), 'pass the reactive descriptor, not a name/value'

Type guard

from textual.reactive import Reactive
def is_reactive(obj: object) -> bool:
    return isinstance(obj, Reactive)

Prevention

When it happens

Trigger: set_reactive('theme', 'dark') (name instead of reactive), set_reactive(MyApp.theme) missing the value pairing, or passing a Data object instead of a reactive attribute. Signature is set_reactive(reactive, value) where reactive is e.g. MyApp.theme (a Reactive accessed on the class).

Common situations: Misreading the API as name/value setter; passing the instance's current value instead of the descriptor; copy-pasting from watch/inspect code that uses strings.

Related errors


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