matplotlib/matplotlib · error · ValueError

outfile must be a path or a file-like object

Error message

outfile must be a path or a file-like object

What it means

The PostScript/EPS renderer accepts only a filesystem path (str or os.PathLike) or a writable file-like object as its output target. Before computing the page layout it checks the type of `outfile` and raises this ValueError when the object is neither. This protects downstream code that would otherwise fail obscurely when passing the value to open() or calling .write() on it.

Source

Thrown at lib/matplotlib/backends/backend_ps.py:1031

        printer(fmt, outfile, dpi=dpi, dsc_comments=dsc_comments,
                orientation=orientation, papertype=papertype,
                bbox_inches_restore=bbox_inches_restore, **kwargs)

    def _print_figure(
            self, fmt, outfile, *,
            dpi, dsc_comments, orientation, papertype,
            bbox_inches_restore=None):
        """
        Render the figure to a filesystem path or a file-like object.

        Parameters are as for `.print_figure`, except that *dsc_comments* is a
        string containing Document Structuring Convention comments,
        generated from the *metadata* parameter to `.print_figure`.
        """
        is_eps = fmt == 'eps'
        if not (isinstance(outfile, (str, os.PathLike))
                or is_writable_file_like(outfile)):
            raise ValueError("outfile must be a path or a file-like object")

        # find the appropriate papertype
        width, height = self.figure.get_size_inches()
        if is_eps or papertype == 'figure':
            paper_width, paper_height = width, height
        else:
            paper_width, paper_height = orientation.swap_if_landscape(
                papersize[papertype])

        # center the figure on the paper
        xo = 72 * 0.5 * (paper_width - width)
        yo = 72 * 0.5 * (paper_height - height)

        llx = xo
        lly = yo
        urx = llx + self.figure.bbox.width
        ury = lly + self.figure.bbox.height
        rotation = 0

View on GitHub (pinned to b379c1b69e)

Solutions

  1. Pass a path string or pathlib.Path, e.g. fig.savefig('out.eps') or fig.savefig(Path('/tmp/out.eps'))
  2. If saving in memory, pass a binary writable buffer: fig.savefig(io.BytesIO(), format='eps')
  3. If passing a file handle, open it in a writable binary mode: open('out.eps', 'wb')
  4. Check the variable you pass — print(type(outfile)) right before the call to confirm it is str/Path/file-like

Example fix

# before
fig.savefig(42, format='eps')  # or savefig(None)

# after
fig.savefig('figure_42.eps', format='eps')
# or in memory:
buf = io.BytesIO()
fig.savefig(buf, format='eps')
Defensive patterns

Strategy: type-guard

Validate before calling

import os

def valid_outfile(obj):
    return isinstance(obj, (str, os.PathLike)) or (
        hasattr(obj, 'write') and callable(obj.write))

# assert valid_outfile(target) before fig.savefig(target, format='eps')

Type guard

from os import PathLike
from typing import Protocol, runtime_checkable

@runtime_checkable
class WritableFileLike(Protocol):
    def write(self, data) -> int: ...

def is_valid_ps_outfile(o: object) -> bool:
    return isinstance(o, (str, PathLike)) or isinstance(o, WritableFileLike)

Try / catch

try:
    fig.savefig(target, format='eps')
except ValueError as e:
    if 'outfile must be a path' in str(e):
        raise TypeError(f'bad save target: {target!r}') from e
    raise

Prevention

When it happens

Trigger: Calling fig.savefig(..., format='ps'/'eps') (or print_ps/print_eps directly) with an outfile that is not a str, pathlib.Path, or an object exposing a writable .write() method — e.g. an int, None, a closed buffer, or a read-only file handle opened with mode 'r'.

Common situations: Scripts that build the output name from a computation that returned None or a number (e.g. savefig(f'fig_{count}' where count is NaN or an int is passed as the whole path), passing io.StringIO (not binary) buffers, or refactoring code that used to write bytes objects. Also passing a file object opened for reading only, which fails the is_writable_file_like check.

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


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