matplotlib/matplotlib · error · RuntimeError

Error starting {mpl.rcParams['pgf.texsystem']!r}

Error message

Error starting {mpl.rcParams['pgf.texsystem']!r}

What it means

Companion to the FileNotFoundError case in _setup_latex_process: any other OSError from subprocess.Popen when starting pgf.texsystem (permission denied, target not executable, resource/spawn limits) is re-raised as RuntimeError(f'Error starting {…!r}') with the original exception chained via __cause__.

Source

Thrown at lib/matplotlib/backends/backend_pgf.py:303

    def _setup_latex_process(self, *, expect_reply=True):
        # Open LaTeX process for real work; register it for deletion.  On
        # Windows, we must ensure that the subprocess has quit before being
        # able to delete the tmpdir in which it runs; in order to do so, we
        # must first `kill()` it, and then `communicate()` with or `wait()` on
        # it.
        try:
            self.latex = subprocess.Popen(
                [mpl.rcParams["pgf.texsystem"], "-halt-on-error", "-no-shell-escape"],
                stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                encoding="utf-8", cwd=self.tmpdir)
        except FileNotFoundError as err:
            raise RuntimeError(
                f"{mpl.rcParams['pgf.texsystem']!r} not found; install it or change "
                f"rcParams['pgf.texsystem'] to an available TeX implementation"
            ) from err
        except OSError as err:
            raise RuntimeError(
                f"Error starting {mpl.rcParams['pgf.texsystem']!r}") from err

        def finalize_latex(latex):
            latex.kill()
            try:
                latex.communicate()
            except RuntimeError:
                latex.wait()

        self._finalize_latex = weakref.finalize(
            self, finalize_latex, self.latex)
        # write header with 'pgf_backend_query_start' token
        self._stdin_writeln(self._build_latex_header())
        if expect_reply:  # read until 'pgf_backend_query_start' token appears
            self._expect("*pgf_backend_query_start")
            self._expect_prompt()

    def get_width_height_descent(self, text, prop):

View on GitHub (pinned to b379c1b69e)

Solutions

  1. Inspect the chained exception: except RuntimeError as e: print(e.__cause__) to see the real OSError
  2. Ensure the binary is executable: chmod +x $(which pdflatex)
  3. Check that the filesystem holding the TeX bin dir is not mounted noexec
  4. Reinstall the TeX distribution if the binary itself is damaged

Example fix

# before: rcParams['pgf.texsystem'] points at /opt/tex/bin/xelatex without +x
#   -> RuntimeError: Error starting 'xelatex'

# after
# chmod +x /opt/tex/bin/xelatex   (or use the distro-packaged engine on PATH)
Defensive patterns

Strategy: validation

Validate before calling

import os
import shutil
import matplotlib as mpl

def texsystem_runnable() -> bool:
    exe = shutil.which(mpl.rcParams['pgf.texsystem'])
    return exe is not None and os.access(exe, os.X_OK)

if not texsystem_runnable():
    raise SystemExit('pgf.texsystem is missing or not executable')

Try / catch

try:
    fig.savefig('out.pgf')
except RuntimeError as err:
    if 'Error starting' not in str(err):
        raise
    raise RuntimeError('cannot start TeX engine') from err.__cause__  # real OSError

Prevention

When it happens

Trigger: pgf.texsystem pointing at a file that exists but lacks the execute bit, a broken wrapper script, a noexec-mounted filesystem holding the TeX binaries, or a security layer (SELinux/AppArmor) denying exec.

Common situations: Manually copied TeX binaries without +x; TeX installed on a network mount or container volume mounted noexec; corrupted installations where the binary fails immediately.

Related errors


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