Textualize/textual · error · KeyError

No key in COMPONENT_CLASSES

Error message

No {name!r} key in COMPONENT_CLASSES

What it means

get_component_styles raises KeyError when asked for a component style name that is not listed in the widget's COMPONENT_CLASSES. Textual requires every component class referenced in code (or matched from CSS like `-border` styling hooks) to be declared up-front so styles can be resolved.

Solutions

  1. Add the component class string to the widget's COMPONENT_CLASSES = [...] class variable exactly as referenced
  2. Fix typos/dashes in the name passed to get_component_styles so it matches an existing COMPONENT_CLASSES entry
  3. If subclassing a built-in widget, remember COMPONENT_CLASSES must be re-declared/extended since it is not inherited additively

Example fix

# before
class MyWidget(Static):
    def render_style(self):
        return self.get_component_styles('mywidget--accent')

# after
class MyWidget(Static):
    COMPONENT_CLASSES = {'mywidget--accent'}
    def render_style(self):
        return self.get_component_styles('mywidget--accent')
Defensive patterns

Strategy: validation

Validate before calling

name = 'mywidget--accent'
if name not in self._component_styles:
    # or: name not in getattr(type(self), 'COMPONENT_CLASSES', ())
    raise LookupError(f'{name!r} missing from COMPONENT_CLASSES')
styles = self.get_component_styles(name)

Type guard

def has_component_class(widget, name: str) -> bool:
    return name in widget._component_styles

Try / catch

try:
    styles = self.get_component_styles('mywidget--accent')
except KeyError as e:
    styles = RenderStyles(self, Styles(), Styles())  # graceful default

Prevention

When it happens

Trigger: Calling self.get_component_styles('foo--bar') where 'foo--bar' is not in the widget's COMPONENT_CLASSES list; CSS referencing a component class the widget never declared; copying CSS between widget types with mismatched COMPONENT_CLASSES.

Common situations: Typos or renaming a component class in CSS but not in COMPONENT_CLASSES; creating a custom widget subclass and forgetting to extend COMPONENT_CLASSES; version upgrades that rename component classes (e.g. tabs, buttons).

Related errors


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

Appendix: source

Thrown at src/textual/dom.py:618

    def get_component_styles(self, *names: str) -> RenderStyles:
        """Get a "component" styles object (must be defined in COMPONENT_CLASSES classvar).

        Args:
            names: Names of the components.

        Raises:
            KeyError: If the component class doesn't exist.

        Returns:
            A Styles object.
        """

        styles = RenderStyles(self, Styles(), Styles())

        for name in names:
            if name not in self._component_styles:
                raise KeyError(f"No {name!r} key in COMPONENT_CLASSES")
            component_styles = self._component_styles[name]
            assert component_styles.node is not None
            styles._update_node(component_styles.node)
            styles.base.merge(component_styles.base)
            styles.inline.merge(component_styles.inline)
            styles._updates += 1

        return styles

    def _post_mount(self):
        """Called after the object has been mounted."""
        _rich_traceback_omit = True
        Reactive._initialize_object(self)

    def notify_style_update(self) -> None:
        """Called after styles are updated.

        Implement this in a subclass if you want to clear any cached data when the CSS is reloaded.

View on GitHub (pinned to 06dbeef4bb)