{"record":{"id":"6dab77f255b2c792","repo":"Textualize/textual","slug":"unable-to-display-obj-class-name-r-type","errorCode":null,"errorMessage":"unable to display {obj.__class__.__name__!r} type; must be a str, Rich renderable, or Textual Visual object","messagePattern":"unable to display (.+?) type; must be a str, Rich renderable, or Textual Visual object","errorType":"exception","errorClass":"VisualError","httpStatus":null,"severity":"error","filePath":"src/textual/visual.py","lineNumber":113,"sourceCode":"        return obj\n    # The visualize method should return a Visual if present.\n    visualize = getattr(obj, \"visualize\", None)\n    if visualize is None:\n        # Doesn't expose the textualize protocol\n        from textual.content import Content\n\n        if isinstance(obj, str):\n            return Content.from_markup(obj) if markup else Content(obj)\n\n        if is_renderable(obj):\n            if isinstance(obj, Text):\n                return Content.from_rich_text(obj, console=widget.app.console)\n\n            # If its is a Rich renderable, wrap it with a RichVisual\n            return RichVisual(widget, rich_cast(obj))\n        else:\n            # We don't know how to make a visual from this object\n            raise VisualError(\n                f\"unable to display {obj.__class__.__name__!r} type; must be a str, Rich renderable, or Textual Visual object\"\n            )\n    # Call the textualize method to create a visual\n    visual = visualize()\n    if not isinstance(visual, Visual) and is_renderable(visual):\n        return RichVisual(widget, visual)\n    return visual\n\n\nclass Visual(ABC):\n    \"\"\"A Textual 'Visual' object.\n\n    Analogous to a Rich renderable, but with support for transparency.\n\n    \"\"\"\n\n    @abstractmethod\n    def render_strips(","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/Textualize/textual/blob/06dbeef4bb70fb718236aa418ed658ef4667a126/src/textual/visual.py#L95-L131","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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 \"\""],"exampleFix":"# before\nwidget.update(self.count)  # int\n# after\nwidget.update(str(self.count))","handlingStrategy":"type-guard","validationCode":"from textual.visual import visualize\ndef displayable(obj) -> bool:\n    return isinstance(obj, (str, Visual)) or is_renderable(obj)","typeGuard":"def is_visualizable(obj: object) -> bool:\n    return (\n        obj is None\n        or isinstance(obj, (str, Visual))\n        or hasattr(obj, \"visualize\")\n        or is_renderable(obj)\n    )","tryCatchPattern":"from textual.visual import VisualError\ntry:\n    widget.update(value)\nexcept VisualError:\n    widget.update(str(value))","preventionTips":["Convert non-renderables to str before update()","Return renderables from render() methods"],"tags":["rendering","visual","type-error","textual"],"backgroundTag":"unsupported-renderable-type","analyzedSha":"06dbeef4bb70fb718236aa418ed658ef4667a126","analyzedAt":"2026-08-27T02:36:57.214Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}