python/cpython · error · ValueError
binary mode doesn't take an encoding argument
Error message
binary mode doesn't take an encoding argument
What it means
Raised by io.open() as a ValueError when binary mode ('b') is combined with a non-None encoding argument. Binary streams carry raw bytes with no codec, so an encoding is meaningless and rejected to prevent silent misuse.
Source
Thrown at Lib/_pyio.py:223
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)
result = raw
try:View on GitHub (pinned to bc6749cc3b)
Solutions
- In binary mode, drop the encoding argument (pass None or omit it).
- In wrappers, forward encoding only for text mode: kwargs = {'encoding': enc} if 'b' not in mode else {}.
- If you actually want decoding, use text mode ('r') instead of 'rb'.
Example fix
// before
def load(path, encoding='utf-8'):
with open(path, 'rb', encoding=encoding) as f: # ValueError
return f.read()
// after
def load(path, encoding='utf-8', binary=False):
mode = 'rb' if binary else 'r'
kwargs = {} if binary else {'encoding': encoding}
with open(path, mode, **kwargs) as f:
return f.read() Defensive patterns
Strategy: validation
Validate before calling
def open_any(path, mode='r', encoding=None, errors=None, newline=None):
if 'b' in mode:
encoding = errors = newline = None # strip text-only kwargs
return open(path, mode, encoding=encoding, errors=errors, newline=newline) Type guard
def kwargs_ok_for_mode(mode: str, **kwargs) -> bool:
return 'b' not in mode or all(v is None for v in (kwargs.get('encoding'),)) Prevention
- In wrappers, build a kwargs dict and only include encoding/errors/newline when 'b' not in mode.
- Decide text vs binary first, then choose the argument set — never pass encoding unconditionally.
- Add a unit test that opens every mode your helper accepts with its default arguments.
When it happens
Trigger: open('f.bin', 'rb', encoding='utf-8'). Very common when encoding is passed unconditionally from a wrapper function (def read(path, encoding='utf-8'): open(path, 'rb', encoding=encoding)) while the mode is caller-controlled.
Common situations: Utility wrappers that always forward an encoding keyword; switching a file from text to binary mode while leaving the encoding argument in place; data-processing defaults like pd-adjacent code that threads encoding through every open call.
Related errors
- can't have text and binary mode at once
- binary mode doesn't take an errors argument
- binary mode doesn't take a newline argument
- invalid encoding: %r
- invalid errors: %r
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/599452b569c244f6.
Report an issue: GitHub.