Textualize/textual · error · TokenError

unknown pseudo-class {pseudo_class!r}; did you mean {suggest

Error message

unknown pseudo-class {pseudo_class!r}; did you mean {suggestion!r}?; must be one of {friendly_list(VALID_PSEUDO_CLASSES)}

What it means

TokenError variant raised when a selector uses a pseudo-class Textual doesn't know, but a close match exists in VALID_PSEUDO_CLASSES — the message offers a did-you-mean suggestion (powered by get_suggestion).

Source

Thrown at src/textual/css/tokenizer.py:326

        token = Token(
            name,
            value,
            self.read_from,
            self.code,
            (line_no, col_no),
            referenced_by=None,
        )

        if (
            token.name == "pseudo_class"
            and token.value.strip(":") not in VALID_PSEUDO_CLASSES
        ):
            pseudo_class = token.value.strip(":")
            suggestion = get_suggestion(pseudo_class, list(VALID_PSEUDO_CLASSES))
            all_valid = f"must be one of {friendly_list(VALID_PSEUDO_CLASSES)}"
            if suggestion:
                raise TokenError(
                    self.read_from,
                    self.code,
                    (line_no + 1, col_no + 1),
                    f"unknown pseudo-class {pseudo_class!r}; did you mean {suggestion!r}?; {all_valid}",
                )
            else:
                raise TokenError(
                    self.read_from,
                    self.code,
                    (line_no + 1, col_no + 1),
                    f"unknown pseudo-class {pseudo_class!r}; {all_valid}",
                )

        col_no += len(value)
        if col_no >= len(line):
            line_no += 1
            col_no = 0
        self.line_no = line_no

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Apply the did-you-mean suggestion in the message
  2. Check the VALID_PSEUDO_CLASSES list in textual/css/tokenizer.py or the docs for supported pseudo-classes
  3. Replace web-CSS pseudo-classes with Textual equivalents (:hover -> :hover, :focus -> :focus, verify spelling)

Example fix

/* before */
Button:pressed { background: blue; }
/* after */
Button:active { background: blue; }
Defensive patterns

Strategy: try-catch

Validate before calling

from textual.css.tokenizer import VALID_PSEUDO_CLASSES
import re
_PSEUDO = re.compile(r'::?[a-zA-Z-]+')
def pseudoclasses_valid(selector: str) -> bool:
    return all(p.strip(':') in VALID_PSEUDO_CLASSES for p in _PSEUDO.findall(selector))

Try / catch

from textual.css.tokenizer import TokenError
try:
    stylesheet.parse()
except TokenError as e:
    if 'unknown pseudo-class' in str(e):
        apply_suggestion(str(e))  # parse the did-you-mean suggestion

Prevention

When it happens

Trigger: Writing selectors like '$button:hovered' (correct is ':hover'), ':focus' (correct ':focusable'/':blur' naming differs from web), ':disabled' etc. — any pseudo-class not in VALID_PSEUDO_CLASSES with a near match.

Common situations: Developers coming from web CSS who assume Textual supports the same pseudo-classes (:hover, :focus, :active) — Textual's names often differ.

Related errors


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