matplotlib/matplotlib · error · TypeError

language must be list of tuple, not {language!r}

Error message

language must be list of tuple, not {language!r}

What it means

Text.set_language() (lib/matplotlib/text.py) validates the libraqm language parameter. After resolving via mpl._val_or_rc(..., 'text.language'), a non-scalar value must be a sequence of 3-tuples (sub-language str, start int, end int) that describe byte ranges with per-range language tags. This TypeError is raised when an element of the sequence is not a tuple of length 3. Note the message is a plain string, not an f-string, so the traceback prints the literal text '{language!r}' instead of your value.

Source

Thrown at lib/matplotlib/text.py:1634

        """
        Set the language of the text.

        Parameters
        ----------
        language : str or None
            The language of the text in a format accepted by libraqm, namely `a BCP47
            language code <https://www.w3.org/International/articles/language-tags/>`_.

            If None, then defaults to :rc:`text.language`.
        """
        _api.check_isinstance((Sequence, str, None), language=language)
        language = mpl._val_or_rc(language, 'text.language')

        if not cbook.is_scalar_or_string(language):
            language = tuple(language)
            for val in language:
                if not isinstance(val, tuple) or len(val) != 3:
                    raise TypeError('language must be list of tuple, not {language!r}')
                sublang, start, end = val
                if not isinstance(sublang, str):
                    raise TypeError(
                        'sub-language specification must be str, not {sublang!r}')
                if not isinstance(start, int):
                    raise TypeError('start location must be int, not {start!r}')
                if not isinstance(end, int):
                    raise TypeError('end location must be int, not {end!r}')

        self._language = language
        self.stale = True


class OffsetFrom:
    """Callable helper class for working with `Annotation`."""

    def __init__(self, artist, ref_coord, unit="points"):
        """

View on GitHub (pinned to b379c1b69e)

Solutions

  1. Pass either a single BCP47 string ('en-US') or a sequence of exactly (str, int, int) tuples, e.g. [('en', 0, 5), ('fr', 6, 11)].
  2. If you only need one language for the whole string, use the scalar form and the range validation disappears.
  3. Remember the printed message does not show the offending value (missing f-prefix) - inspect the argument you passed rather than the message text.
  4. Ranges are byte offsets into the UTF-8 text - compute them with len(text[:i].encode('utf-8')), not character counts, or libraqm will mis-tag.

Example fix

# before
t.set_language(['en-US', 'fr-FR'])  # TypeError: not 3-tuples

# after
t.set_language([('en', 0, 5), ('fr', 6, 11)])
Defensive patterns

Strategy: type-guard

Type guard

def valid_language(v):
    if v is None or isinstance(v, str):
        return True
    try:
        return all(
            isinstance(t, tuple) and len(t) == 3
            and isinstance(t[0], str) and isinstance(t[1], int) and isinstance(t[2], int)
            for t in v)
    except TypeError:
        return False

Prevention

When it happens

Trigger: set_language(['en-us']); set_language([('en', 0)]) (2-tuple); set_language([('en', 0, 5), 'fr']) (mixed). Scalar strings and None are accepted and never reach this check; only Sequence inputs are iterated.

Common situations: Using raqm-based complex-text rendering with mixed scripts and constructing the range list by zipping columns that have unequal lengths; passing a list of language tags only (missing the start/end indices); reading the spec as 'list of (lang, start, end)' but storing dicts instead of tuples.

Related errors


AI-assisted analysis of matplotlib/matplotlib@b379c1b69e (2026-08-21). Data as JSON: /api/errors/7afe2009c0dbc400. Report an issue: GitHub.