python/cpython · error · ValueError
must have exactly one of read/write/append mode
Error message
must have exactly one of read/write/append mode
What it means
Raised by io.open() as a ValueError when the mode string contains none of the base operations r/w/a/x. open() requires exactly one base operation; auxiliary flags ('+', 'b', 't') alone do not define what to do with the file.
Source
Thrown at Lib/_pyio.py:221
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")
if creating + reading + writing + appending > 1:
raise ValueError("can't have read/write/append mode at once")
if not (creating or reading or writing or appending):
raise ValueError("must have exactly one of read/write/append mode")
if binary and encoding is not None:
raise ValueError("binary mode doesn't take an encoding argument")
if binary and errors is not None:
raise ValueError("binary mode doesn't take an errors argument")
if binary and newline is not None:
raise ValueError("binary mode doesn't take a newline argument")
if binary and buffering == 1:
import warnings
warnings.warn("line buffering (buffering=1) isn't supported in binary "
"mode, the default buffer size will be used",
RuntimeWarning, 2)
raw = FileIO(file,
(creating and "x" or "") +
(reading and "r" or "") +
(writing and "w" or "") +
(appending and "a" or "") +
(updating and "+" or ""),
closefd, opener=opener)View on GitHub (pinned to bc6749cc3b)
Solutions
- Always include a base letter: 'r' is the default — use 'rb', 'r+b', or just omit mode for 'r'.
- Give mode-building code a fallback base: base = 'r' if not (write or append) else ...
- Assert the final mode intersects {'r','w','a','x'} before calling open().
Example fix
// before
mode = ('b' if binary else '') + ('+' if rw else '') # can be '' or 'b'
open(path, mode)
// after
base = 'w' if create else 'r'
mode = base + ('b' if binary else '') + ('+' if rw else '')
open(path, mode) Defensive patterns
Strategy: validation
Validate before calling
def build_mode(binary: bool = False, update: bool = False, base: str = 'r') -> str:
if base not in ('r', 'w', 'a', 'x'):
raise ValueError(f'base must be one of r/w/a/x, got {base!r}')
return base + ('b' if binary else '') + ('+' if update else '') Type guard
def has_base_op(mode: str) -> bool:
return bool(set(mode) & {'r', 'w', 'a', 'x'}) Prevention
- Always give mode-building helpers a non-optional base letter (default 'r').
- Remember open() itself defaults mode='r' — omit the argument rather than passing '' or flags-only strings.
- Assert the final composed mode is non-empty and contains a base letter.
When it happens
Trigger: open('f', 'b'), open('f', '+'), open('f', 't'), open('f', '') or open('f', '+b'). Typically a mode variable that ended up empty (e.g. ''.join of flags where the base letter was forgotten) or a default of None coerced to '' somewhere.
Common situations: Mode built from optional flags where the base flag is itself optional and absent: mode = ('r' if read else '') + 'b' with read=False gives 'b'; refactors that drop the base letter but keep '+' / 'b'; passing an empty string explicitly.
Related errors
- can't have text and binary mode at once
- can't have read/write/append mode at once
- unknown mode: %r
- invalid mode: %r
- binary mode doesn't take an encoding argument
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/dedde04b5edd7902.
Report an issue: GitHub.