python/cpython · error · TypeError

invalid mode: %s

Error message

invalid mode: %s

What it means

Raised by FileIO.__init__ when the mode argument is not a str instance ('invalid mode: %s' % (mode,)). This is a TypeError raised before any mode-character validation, so even a plausible mode like b'rb' or a tuple fails here.

Source

Thrown at Lib/_pyio.py:1560

            finally:
                self._fd = -1

        if isinstance(file, float):
            raise TypeError('integer argument expected, got float')
        if isinstance(file, int):
            if isinstance(file, bool):
                import warnings
                warnings.warn("bool is used as a file descriptor",
                              RuntimeWarning, stacklevel=2)
                file = int(file)
            fd = file
            if fd < 0:
                raise ValueError('negative file descriptor')
        else:
            fd = -1

        if not isinstance(mode, str):
            raise TypeError('invalid mode: %s' % (mode,))
        if not set(mode) <= set('xrwab+'):
            raise ValueError('invalid mode: %s' % (mode,))
        if sum(c in 'rwax' for c in mode) != 1 or mode.count('+') > 1:
            raise ValueError('Must have exactly one of create/read/write/append '
                             'mode and at most one plus')

        if 'x' in mode:
            self._created = True
            self._writable = True
            flags = os.O_EXCL | os.O_CREAT
        elif 'r' in mode:
            self._readable = True
            flags = 0
        elif 'w' in mode:
            self._writable = True
            self._truncate = True
            flags = os.O_CREAT | os.O_TRUNC
        elif 'a' in mode:

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Pass mode as a plain str like 'rb', 'wb', 'r+b'
  2. Normalize config values: mode = str(mode) if mode is not None else 'r'
  3. Check for swapped positional arguments (file, mode) in the call

Example fix

# before
f = io.FileIO('data.bin', b'rb')  # TypeError: invalid mode: b'rb'

# after
f = io.FileIO('data.bin', 'rb')
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(mode, str):
    raise TypeError(f'mode must be str, got {type(mode).__name__}')
f = io.FileIO(path, mode)

Type guard

def is_mode_str(m):
    return isinstance(m, str) and len(m) > 0

Try / catch

try:
    f = io.FileIO(path, mode)
except TypeError as e:
    if 'invalid mode' in str(e):
        f = io.FileIO(path, str(mode))
    else:
        raise

Prevention

When it happens

Trigger: open(path, b'rb', buffering=0) via io.FileIO; passing mode as None, an enum object, or bytes; a keyword mix-up where a non-string positional argument lands in the mode slot.

Common situations: Config-driven mode values loaded from JSON/YAML as non-strings; mixing bytes and str mode constants in code migrated from Python 2; passing an IntEnum or custom Mode class where a plain string is required.

Related errors


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