Textualize/rich · error · ValueError

'characters' argument must have a cell width of at least 1

Error message

'characters' argument must have a cell width of at least 1

What it means

Rule renders a horizontal line built by repeating its characters= string. rich validates that the string has a display (cell) width of at least 1, measured with cell_len — which understands East Asian wide characters and zero-width joins. A string of only zero-width/combining characters (cell width 0) cannot draw a line, so Rule raises ValueError at construction.

Source

Thrown at rich/rule.py:33

    Args:
        title (Union[str, Text], optional): Text to render in the rule. Defaults to "".
        characters (str, optional): Character(s) used to draw the line. Defaults to "─".
        style (StyleType, optional): Style of Rule. Defaults to "rule.line".
        end (str, optional): Character at end of Rule. defaults to "\\\\n"
        align (str, optional): How to align the title, one of "left", "center", or "right". Defaults to "center".
    """

    def __init__(
        self,
        title: Union[str, Text] = "",
        *,
        characters: str = "─",
        style: Union[str, Style] = "rule.line",
        end: str = "\n",
        align: AlignMethod = "center",
    ) -> None:
        if cell_len(characters) < 1:
            raise ValueError(
                "'characters' argument must have a cell width of at least 1"
            )
        if align not in ("left", "center", "right"):
            raise ValueError(
                f'invalid value for align, expected "left", "center", "right" (not {align!r})'
            )
        self.title = title
        self.characters = characters
        self.style = style
        self.end = end
        self.align = align

    def __repr__(self) -> str:
        return f"Rule({self.title!r}, {self.characters!r})"

    def __rich_console__(
        self, console: Console, options: ConsoleOptions
    ) -> RenderResult:

View on GitHub (pinned to 9d8f9a372c)

Solutions

  1. Default to '─' (U+2500) or pass a real visible character: Rule('Title', characters='=') .
  2. Validate config-supplied separators before use: if not chars or cell_len(chars) < 1: use fallback.
  3. For an invisible divider, use a space-padded character like ' ' plus a style, or render a blank Text instead of a zero-width Rule.

Example fix

# before
print(Rule('Header', characters=''))  # ValueError: cell width of at least 1

# after
chars = cfg.get('rule_chars') or '─'
print(Rule('Header', characters=chars))
Defensive patterns

Strategy: validation

Validate before calling

from rich.cells import cell_len
chars = cfg.get('rule_chars', '')
if cell_len(chars) < 1:
    chars = '─'
rule = Rule('Title', characters=chars)

Type guard

def is_valid_rule_characters(chars: str) -> bool:
    return isinstance(chars, str) and cell_len(chars) >= 1

Prevention

When it happens

Trigger: Rule(characters=''), Rule(characters='\u200b') (zero-width space), Rule(characters='\u0301') (combining mark), or any sequence whose combined cell width is 0. Note: a string like '──' is fine; the check is width, not count.

Common situations: User-configurable separator characters (CLI themes, config files) where an empty string slips through; loading separator settings from YAML/JSON where the value is blank; passing a stripped/normalized string that ended up empty; characters read from a source that lost a zero-width glyph.

Related errors


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