{"record":{"id":"55c0fc811df545b7","repo":"matplotlib/matplotlib","slug":"outfile-must-be-a-path-or-a-file-like-object","errorCode":null,"errorMessage":"outfile must be a path or a file-like object","messagePattern":"outfile must be a path or a file-like object","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/matplotlib/backends/backend_ps.py","lineNumber":1031,"sourceCode":"        printer(fmt, outfile, dpi=dpi, dsc_comments=dsc_comments,\n                orientation=orientation, papertype=papertype,\n                bbox_inches_restore=bbox_inches_restore, **kwargs)\n\n    def _print_figure(\n            self, fmt, outfile, *,\n            dpi, dsc_comments, orientation, papertype,\n            bbox_inches_restore=None):\n        \"\"\"\n        Render the figure to a filesystem path or a file-like object.\n\n        Parameters are as for `.print_figure`, except that *dsc_comments* is a\n        string containing Document Structuring Convention comments,\n        generated from the *metadata* parameter to `.print_figure`.\n        \"\"\"\n        is_eps = fmt == 'eps'\n        if not (isinstance(outfile, (str, os.PathLike))\n                or is_writable_file_like(outfile)):\n            raise ValueError(\"outfile must be a path or a file-like object\")\n\n        # find the appropriate papertype\n        width, height = self.figure.get_size_inches()\n        if is_eps or papertype == 'figure':\n            paper_width, paper_height = width, height\n        else:\n            paper_width, paper_height = orientation.swap_if_landscape(\n                papersize[papertype])\n\n        # center the figure on the paper\n        xo = 72 * 0.5 * (paper_width - width)\n        yo = 72 * 0.5 * (paper_height - height)\n\n        llx = xo\n        lly = yo\n        urx = llx + self.figure.bbox.width\n        ury = lly + self.figure.bbox.height\n        rotation = 0","sourceCodeStart":1013,"sourceCodeEnd":1049,"githubUrl":"https://github.com/matplotlib/matplotlib/blob/b379c1b69e012b142c0f496a52bcb30513802d72/lib/matplotlib/backends/backend_ps.py#L1013-L1049","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","solutions":["Pass a path string or pathlib.Path, e.g. fig.savefig('out.eps') or fig.savefig(Path('/tmp/out.eps'))","If saving in memory, pass a binary writable buffer: fig.savefig(io.BytesIO(), format='eps')","If passing a file handle, open it in a writable binary mode: open('out.eps', 'wb')","Check the variable you pass — print(type(outfile)) right before the call to confirm it is str/Path/file-like"],"exampleFix":"# before\nfig.savefig(42, format='eps')  # or savefig(None)\n\n# after\nfig.savefig('figure_42.eps', format='eps')\n# or in memory:\nbuf = io.BytesIO()\nfig.savefig(buf, format='eps')","handlingStrategy":"type-guard","validationCode":"import os\n\ndef valid_outfile(obj):\n    return isinstance(obj, (str, os.PathLike)) or (\n        hasattr(obj, 'write') and callable(obj.write))\n\n# assert valid_outfile(target) before fig.savefig(target, format='eps')","typeGuard":"from os import PathLike\nfrom typing import Protocol, runtime_checkable\n\n@runtime_checkable\nclass WritableFileLike(Protocol):\n    def write(self, data) -> int: ...\n\ndef is_valid_ps_outfile(o: object) -> bool:\n    return isinstance(o, (str, PathLike)) or isinstance(o, WritableFileLike)","tryCatchPattern":"try:\n    fig.savefig(target, format='eps')\nexcept ValueError as e:\n    if 'outfile must be a path' in str(e):\n        raise TypeError(f'bad save target: {target!r}') from e\n    raise","preventionTips":["Always build save targets as f-strings or pathlib.Path, never pass raw computed values unchecked","Log type(outfile) in debug output before saving","Standardize on Path objects for all save paths in the codebase"],"tags":["matplotlib","savefig","postscript","eps","type-validation"],"backgroundTag":"invalid-argument-type","analyzedSha":"b379c1b69e012b142c0f496a52bcb30513802d72","analyzedAt":"2026-08-21T23:31:55.468Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}