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

  1. Convert the value to str (or format it) before display
  2. Return a proper renderable (Text, Table, Visual) from render/visual methods
  3. 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

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


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