Textualize/rich · error · ValueError

invalid value for vertical, expected "top", "middle", or "bo

Error message

invalid value for vertical, expected "top", "middle", or "bottom" (not {vertical!r})

What it means

Align.__init__ validates the keyword-only vertical argument: it must be None or one of "top", "middle", "bottom", otherwise ValueError. Unlike align, vertical is optional and defaults to None (no vertical alignment). The literal "middle" is used here, not "center" — a common source of confusion since horizontal alignment uses "center".

Source

Thrown at rich/align.py:63

    """

    def __init__(
        self,
        renderable: "RenderableType",
        align: AlignMethod = "left",
        style: Optional[StyleType] = None,
        *,
        vertical: Optional[VerticalAlignMethod] = None,
        pad: bool = True,
        width: Optional[int] = None,
        height: Optional[int] = None,
    ) -> None:
        if align not in ("left", "center", "right"):
            raise ValueError(
                f'invalid value for align, expected "left", "center", or "right" (not {align!r})'
            )
        if vertical is not None and vertical not in ("top", "middle", "bottom"):
            raise ValueError(
                f'invalid value for vertical, expected "top", "middle", or "bottom" (not {vertical!r})'
            )
        self.renderable = renderable
        self.align = align
        self.style = style
        self.vertical = vertical
        self.pad = pad
        self.width = width
        self.height = height

    def __repr__(self) -> str:
        return f"Align({self.renderable!r}, {self.align!r})"

    @classmethod
    def left(
        cls,
        renderable: "RenderableType",
        style: Optional[StyleType] = None,

View on GitHub (pinned to 9d8f9a372c)

Solutions

  1. Use exactly "top", "middle", or "bottom" (note: "middle", not "center")
  2. Omit the vertical argument or pass None if no vertical alignment is wanted
  3. Normalize user config values (strip().lower()) before passing them in

Example fix

// before (Python)
Align(panel, vertical="center")  # ValueError
// after
Align(panel, vertical="middle")  # or omit / pass None
Defensive patterns

Strategy: type-guard

Validate before calling

vertical = None if raw in (None, "") else str(raw).strip().lower()
if vertical is not None and vertical not in ("top", "middle", "bottom"):
    raise ValueError(f"vertical must be top/middle/bottom, got {raw!r}")
render = Align(panel, vertical=vertical)

Type guard

from typing import Literal, TypeGuard

VerticalAlignMethod = Literal["top", "middle", "bottom"]

def is_vertical_align(v: object) -> TypeGuard[VerticalAlignMethod]:
    return v in ("top", "middle", "bottom")

Prevention

When it happens

Trigger: Calling Align(renderable, vertical="center") (mixing up the horizontal term), vertical="top " with stray whitespace, or passing an empty string instead of None. Passing a vertical value via unpacked kwargs from a settings dict is another common route.

Common situations: Developers writing vertical="center" by analogy with align="center"; config-driven layouts where "center" is used for both axes; forgetting that None (not "") disables vertical alignment.

Related errors


AI-assisted analysis of Textualize/rich@9d8f9a372c (2026-08-15). Data as JSON: /api/errors/c638e107e8fc791f. Report an issue: GitHub.