python/cpython · error · TypeError
invalid mode: %r
Error message
invalid mode: %r
What it means
Raised by io.open() when the mode argument is not a str. open() validates argument types before touching the filesystem; any non-string mode (None, bytes, int) triggers this TypeError. Note this is about the type, not the contents — a malformed string like 'z' raises ValueError('invalid mode') instead.
Source
Thrown at Lib/_pyio.py:199
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
if text and binary:
raise ValueError("can't have text and binary mode at once")View on GitHub (pinned to bc6749cc3b)
Solutions
- Pass a str mode such as 'r', 'w', 'a', 'x', 'rb', 'w+b', etc.
- If mode is computed, give it a default ('r' is open()'s default) and assert isinstance(mode, str) near the computation.
- Fix the None-returning expression (e.g. regex .group() vs .group(0), dict.get with default) that feeds the mode parameter.
Example fix
// before
mode = flags.get('write') and 'w' # None when 'write' missing
open('out.txt', mode) # TypeError: invalid mode: None
// after
mode = 'w' if flags.get('write') else 'r'
open('out.txt', mode) Defensive patterns
Strategy: validation
Validate before calling
VALID_MODES = {'r','w','a','x','r+','w+','a+','x+','rb','wb','ab','xb','r+b','w+b','a+b','x+b','rt','wt','at','xt','r+t','w+t','a+t','x+t'}
assert mode in VALID_MODES, f'unexpected mode {mode!r}'
open(path, mode) Type guard
def is_valid_mode(mode) -> bool:
return (
isinstance(mode, str)
and set(mode) <= set('axrwb+t')
and len(mode) == len(set(mode))
and bool(set(mode) & set('rwax'))
and not ({'t','b'} <= set(mode))
) Try / catch
try:
f = open(path, mode)
except TypeError as e:
if 'invalid mode' in str(e):
mode = 'r' # safe fallback
f = open(path, mode)
else:
raise Prevention
- Default mode explicitly (mode='r') in wrappers so it is never None.
- Build modes only from a whitelist of complete strings ('rb', 'w+', ...), never by free-form concatenation.
- Check regex group results for None before using them as mode.
When it happens
Trigger: open('f.txt', None), open('f.txt', b'r'), open('f.txt', 0), or a typo like open('f.txt', modes) where modes is a set/list of characters built elsewhere. Also passing mode as a keyword with the wrong variable, e.g. open(path, mode=m.group(1)) where the regex group is None on no-match.
Common situations: Mode computed dynamically (from config or CLI flags) and the computation silently yields None; refactoring that reorders positional args so a non-mode value lands in the mode slot; copy-paste from bytes-literal code (b'r') in porting exercises.
Related errors
- invalid file: %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/38d20732913f8be2.
Report an issue: GitHub.