Textualize/textual · error · VisualError
unable to display {obj.__class__.__name__!r} type; must be a
Error message
unable to display {obj.__class__.__name__!r} type; must be a str, Rich renderable, or Textual Visual object What it means
visualize() can only convert strings, Rich renderables, Textual Visuals (and Visualize-protocol objects) into a Visual; any other type raises VisualError with the offending class name.
Source
Thrown at src/textual/visual.py:113
return obj
# The visualize method should return a Visual if present.
visualize = getattr(obj, "visualize", None)
if visualize is None:
# Doesn't expose the textualize protocol
from textual.content import Content
if isinstance(obj, str):
return Content.from_markup(obj) if markup else Content(obj)
if is_renderable(obj):
if isinstance(obj, Text):
return Content.from_rich_text(obj, console=widget.app.console)
# If its is a Rich renderable, wrap it with a RichVisual
return RichVisual(widget, rich_cast(obj))
else:
# We don't know how to make a visual from this object
raise VisualError(
f"unable to display {obj.__class__.__name__!r} type; must be a str, Rich renderable, or Textual Visual object"
)
# Call the textualize method to create a visual
visual = visualize()
if not isinstance(visual, Visual) and is_renderable(visual):
return RichVisual(widget, visual)
return visual
class Visual(ABC):
"""A Textual 'Visual' object.
Analogous to a Rich renderable, but with support for transparency.
"""
@abstractmethod
def render_strips(View on GitHub (pinned to 06dbeef4bb)
Solutions
- Convert the value to str (or format it) before display
- Return a proper renderable (Text, Table, Visual) from render/visual methods
- Handle None with a fallback like value or ""
Example fix
# before widget.update(self.count) # int # after widget.update(str(self.count))
Defensive patterns
Strategy: type-guard
Validate before calling
from textual.visual import visualize
def displayable(obj) -> bool:
return isinstance(obj, (str, Visual)) or is_renderable(obj) Type guard
def is_visualizable(obj: object) -> bool:
return (
obj is None
or isinstance(obj, (str, Visual))
or hasattr(obj, "visualize")
or is_renderable(obj)
) Try / catch
from textual.visual import VisualError
try:
widget.update(value)
except VisualError:
widget.update(str(value)) Prevention
- Convert non-renderables to str before update()
- Return renderables from render() methods
When it happens
Trigger: Passing an int, dict, None, or arbitrary object where renderable content is expected: widget.update(42), content=..., or returning non-renderables from render methods that get visualized.
Common situations: update() called with numbers or data structures instead of str; helper returning None from a conditional; version upgrades where content now goes through the Visual pipeline.
Related errors
- {self.name} must be a str
- Invalid color value {color}
- Widget positional arguments must be Widget subclasses; not {
- value must be a str
- `before` argument must be an index or a TreeNode object to a
AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27).
Data as JSON: /api/errors/6dab77f255b2c792.
Report an issue: GitHub.