3b1b/manim · error · ValueError

Invalid markup string \"{markup_str}\"\n{validate_error}

Error message

Invalid markup string \"{markup_str}\"\n{validate_error}

What it means

Raised by markup_to_svg (text_mobject.py:64) when manimpango's MarkupUtils.validate reports the string is not valid Pango markup. Text() uses Pango markup, so raw '&', '<', '>' and unknown/mismatched tags (<tts>, <b> without </b>) all fail validation before rendering.

Source

Thrown at manimlib/mobject/svg/text_mobject.py:64

        "RIGHT": 2
    }

    def __init__(self, s: str):
        self.value = _Alignment.VAL_DICT[s.upper()]


@lru_cache(maxsize=128)
@cache_on_disk
def markup_to_svg(
    markup_str: str,
    justify: bool = False,
    indent: float = 0,
    alignment: str = "CENTER",
    line_width: float | None = None,
) -> str:
    validate_error = manimpango.MarkupUtils.validate(markup_str)
    if validate_error:
        raise ValueError(
            f"Invalid markup string \"{markup_str}\"\n" + \
            f"{validate_error}"
        )

    # `manimpango` is under construction,
    # so the following code is intended to suit its interface
    alignment = _Alignment(alignment)
    if line_width is None:
        pango_width = -1
    else:
        pango_width = line_width / FRAME_WIDTH * DEFAULT_PIXEL_WIDTH

    # Write the result to a temporary svg file, and return it's contents.
    temp_file = Path(tempfile.gettempdir(), hash_string(markup_str)).with_suffix(".svg")
    manimpango.MarkupUtils.text2svg(
        text=markup_str,
        font="",                     # Already handled
        slant="NORMAL",              # Already handled

View on GitHub (pinned to dee01804d4)

Solutions

  1. Escape special characters with html.escape()/xml.sax.saxutils.escape() before passing user data to Text()
  2. Fix the markup tags: every opened tag must be closed and must be a Pango-supported tag (b, i, u, s, sub, sup, span, ...)
  3. For literal angle brackets/ampersands use &lt; &gt; &amp; entities

Example fix

# before
Text('Tom & Jerry <3')  # invalid markup -> raises

# after
import html
Text(html.escape('Tom & Jerry <3'))
Defensive patterns

Strategy: validation

Validate before calling

from xml.sax.saxutils import escape
import re

def safe_markup(s: str) -> str:
    # escape bare & < > unless the string already looks like deliberate markup
    if re.search(r'<(b|i|u|s|sub|sup|span)\b', s):
        return s  # author-supplied markup; trust it
    return escape(s)

Text(safe_markup(user_string))

Try / catch

import manimpango
try:
    Text(markup_str)
except ValueError as e:
    if 'Invalid markup string' in str(e):
        from xml.sax.saxutils import escape
        Text(escape(markup_str))

Prevention

When it happens

Trigger: Text('A & B'); Text('<b>bold'); Text('<tts>hi</tts>') with an unsupported tag; markup built by string concatenation that leaves a dangling '<'. Markup like '<b>bold</b>' and '<i>i</i>' is valid.

Common situations: Rendering user-supplied or dynamically composed strings containing XML special characters; switching from Tex/PlainText assumptions to Text() which parses markup; version changes in manimpango that alter the accepted tag set.

Related errors


AI-assisted analysis of 3b1b/manim@dee01804d4 (2026-08-14). Data as JSON: /api/errors/6844c4c80183997f. Report an issue: GitHub.