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
- 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)].
- If you only need one language for the whole string, use the scalar form and the range validation disappears.
- Remember the printed message does not show the offending value (missing f-prefix) - inspect the argument you passed rather than the message text.
- 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
- Use the scalar BCP47 string form unless you truly need per-range sub-languages.
- Build range tuples explicitly as (lang, start, end) with int byte offsets.
- Remember the error message prints literal '{language!r}' (missing f-prefix) - debug from your inputs, not the text.
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
- sub-language specification must be str, not {sublang!r}
- start location must be int, not {start!r}
- end location must be int, not {end!r}
- format must be callable or a format string
- rotation must be 'vertical', 'horizontal' or a number, not {
AI-assisted analysis of matplotlib/matplotlib@b379c1b69e (2026-08-21).
Data as JSON: /api/errors/7afe2009c0dbc400.
Report an issue: GitHub.