Textualize/textual · error · InvalidPlaceholderVariant

Valid placeholder variants are {friendly_list(_VALID_PLACEHO

Error message

Valid placeholder variants are {friendly_list(_VALID_PLACEHOLDER_VARIANTS)}

What it means

Raised by Placeholder.validate_variant when the variant reactive is set to a string not in _VALID_PLACEHOLDER_VARIANTS (e.g. 'default', 'cross', 'size', 'text-widgets', etc.). Because it is a reactive validator it triggers on __init__ or assignment to placeholder.variant.

Source

Thrown at src/textual/widgets/_placeholder.py:172

    def cycle_variant(self) -> Self:
        """Get the next variant in the cycle.

        Returns:
            The `Placeholder` instance.
        """
        self.variant = next(self._variants_cycle)
        return self

    def watch_variant(
        self, old_variant: PlaceholderVariant, variant: PlaceholderVariant
    ) -> None:
        self.remove_class(f"-{old_variant}")
        self.add_class(f"-{variant}")

    def validate_variant(self, variant: PlaceholderVariant) -> PlaceholderVariant:
        """Validate the variant to which the placeholder was set."""
        if variant not in _VALID_PLACEHOLDER_VARIANTS:
            raise InvalidPlaceholderVariant(
                "Valid placeholder variants are "
                + f"{friendly_list(_VALID_PLACEHOLDER_VARIANTS)}"
            )
        return variant

    async def _on_click(self, _: events.Click) -> None:
        """Click handler to cycle through the placeholder variants."""
        self.cycle_variant()

    def _on_resize(self, event: events.Resize) -> None:
        """Update the placeholder "size" variant with the new placeholder size."""
        self._renderables["size"] = self._SIZE_RENDER_TEMPLATE.format(*event.size)
        if self.variant == "size":
            self.refresh()

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Use one of the documented variants: check _VALID_PLACEHOLDER_VARIANTS in textual/widgets/_placeholder.py for the exact set.
  2. Pass no variant to use the default.
  3. Fix typos like 'textwidget' vs 'text-widgets'.

Example fix

# before
Placeholder(variant='textwidget')
# after
Placeholder(variant='text-widgets')
Defensive patterns

Strategy: type-guard

Validate before calling

from textual.widgets._placeholder import _VALID_PLACEHOLDER_VARIANTS
if variant not in _VALID_PLACEHOLDER_VARIANTS:
    variant = 'default'

Type guard

def is_valid_variant(v: str) -> bool: return v in _VALID_PLACEHOLDER_VARIANTS

Prevention

When it happens

Trigger: Placeholder(variant='primary') or placeholder.variant = 'normal' — any misspelled or unsupported variant name.

Common situations: Typos, or assuming variant names from other libraries/CSS class names apply; upgrading Textual where the valid variant set changed.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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