python/cpython · error · TypeError

invalid file: %r

Error message

invalid file: %r

What it means

Raised by io.open() (the _pyio reference implementation of builtins.open) when the file argument cannot be reduced to a path or file descriptor. open() first passes file through os.fspath(); if the result is not str, bytes, or an int (fd number), this TypeError is raised. It is a type-validation error at the API boundary, before any filesystem access happens.

Source

Thrown at Lib/_pyio.py:197

    open() returns a file object whose type depends on the mode, and
    through which the standard file operations such as reading and writing
    are performed. When open() is used to open a file in a text mode ('w',
    'r', 'wt', 'rt', etc.), it returns a TextIOWrapper. When used to open
    a file in a binary mode, the returned class varies: in read binary
    mode, it returns a BufferedReader; in write binary and append binary
    modes, it returns a BufferedWriter, and in read/write mode, it returns
    a BufferedRandom.

    It is also possible to use a string or bytearray as a file for both
    reading and writing. For strings StringIO can be used like a file
    opened in a text mode, and for bytes a BytesIO can be used like a file
    opened in a binary mode.
    """
    if not isinstance(file, int):
        file = os.fspath(file)
    if not isinstance(file, (str, bytes, int)):
        raise TypeError("invalid file: %r" % file)
    if not isinstance(mode, str):
        raise TypeError("invalid mode: %r" % mode)
    if not isinstance(buffering, int):
        raise TypeError("invalid buffering: %r" % buffering)
    if encoding is not None and not isinstance(encoding, str):
        raise TypeError("invalid encoding: %r" % encoding)
    if errors is not None and not isinstance(errors, str):
        raise TypeError("invalid errors: %r" % errors)
    modes = set(mode)
    if modes - set("axrwb+t") or len(mode) > len(modes):
        raise ValueError("invalid mode: %r" % mode)
    creating = "x" in modes
    reading = "r" in modes
    writing = "w" in modes
    appending = "a" in modes
    updating = "+" in modes
    text = "t" in modes
    binary = "b" in modes

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Check where the value came from: it is None or a container, not a path — fix the upstream assignment (config key, argparse argument, function return).
  2. Pass a str, bytes, os.PathLike (e.g. pathlib.Path), or an int file descriptor to open().
  3. If you wrote the custom class being passed, make its __fspath__ return str or bytes.
  4. Loop over path collections: for p in paths: with open(p) as f: ... instead of open(paths).

Example fix

// before
path = config.get('logfile')  # returns None when key missing
f = open(path)  # TypeError: invalid file: None

// after
path = config['logfile']  # fail fast on missing key, or:
if not isinstance(path, (str, bytes, os.PathLike)):
    raise TypeError(f'expected a path, got {path!r}')
f = open(path)
Defensive patterns

Strategy: type-guard

Validate before calling

import os

def validate_path(file):
    if isinstance(file, int):
        return file          # file descriptor
    p = os.fspath(file) if isinstance(file, os.PathLike) else file
    if not isinstance(p, (str, bytes)):
        raise TypeError(f'path must be str/bytes/PathLike/fd, got {type(file).__name__}')
    return p

Type guard

import os

def is_openable(file) -> bool:
    if isinstance(file, int):
        return True
    try:
        p = os.fspath(file)
    except TypeError:
        return False
    return isinstance(p, (str, bytes))

Try / catch

try:
    f = open(target)
except TypeError as e:
    # 'invalid file' — target was None/list/etc.
    raise ValueError(f'bad path from config: {target!r}') from None

Prevention

When it happens

Trigger: open(None), open(['a.txt']), open({'path': 'a.txt'}), or an os.PathLike class whose __fspath__ returns a non-str/bytes value (e.g. returns None or an int). Also passing a closed/garbage object where a path was expected, e.g. open(some_result) where some_result is None because a lookup failed.

Common situations: A variable expected to hold a filename is None because an earlier config lookup, argparse default, or function return was missed; passing a list of paths to open() instead of looping; custom PathLike wrappers (mocks in tests, lazy-path objects) whose __fspath__ has the wrong return type.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/5a81dee2100dcba2. Report an issue: GitHub.