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 modesView on GitHub (pinned to bc6749cc3b)
Solutions
- 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).
- Pass a str, bytes, os.PathLike (e.g. pathlib.Path), or an int file descriptor to open().
- If you wrote the custom class being passed, make its __fspath__ return str or bytes.
- 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
- Fail fast on config/argparse lookups so path variables are never None (use [] with defaults, not .get).
- Type-annotate path parameters as str | os.PathLike and check at boundaries.
- Loop over collections of paths; never pass a list/dict where one path is expected.
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
- invalid mode: %r
- invalid buffering: %r
- invalid encoding: %r
- invalid errors: %r
- can't have text and binary mode at once
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/5a81dee2100dcba2.
Report an issue: GitHub.