Textualize/textual · error · KeyError
No {name!r} 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.
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)
Solutions
- Add the component class string to the widget's COMPONENT_CLASSES = [...] class variable exactly as referenced
- Fix typos/dashes in the name passed to get_component_styles so it matches an existing COMPONENT_CLASSES entry
- 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
- Declare every component class used in CSS/code in COMPONENT_CLASSES
- Keep CSS and COMPONENT_CLASSES in one commit when renaming
- Add a startup test iterating COMPONENT_CLASSES-referencing CSS
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
- Tried to insert a widget with ID {widget_id!r}, but a widget
- Expected a str, Path or list[str | Path] for the CSS_PATH.
- {self.name} must be one of {friendly_list(self._valid_values
- {self.name} must be a str
- invalid percentage value '{token}'
AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27).
Data as JSON: /api/errors/74d6013d273136e6.
Report an issue: GitHub.