python/cpython · error · ValueError

can't have text and binary mode at once

Error message

can't have text and binary mode at once

What it means

Raised by io.open() as a ValueError when the mode string contains both 't' and 'b'. Text mode and binary mode are mutually exclusive: text mode decodes bytes through an encoding with newline translation, binary mode returns raw bytes, so both cannot apply at once.

Source

Thrown at Lib/_pyio.py:217

        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")
    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 "") +

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Pick one: 'rb'/'wb'/'ab' (+ 'b' variants) for bytes, or 'r'/'w'/'a' with optional 't' for str.
  2. In mode-building code, derive binary/text from a single variable, not two independent flags that can both be true.
  3. Validate composed modes against a whitelist before calling open().

Example fix

// before
mode = base_mode + ('t' if text_default else '')   # base 'rb' -> 'rbt'
open(path, mode)

// after
if 'b' in base_mode:
    mode = base_mode            # binary stays binary
else:
    mode = base_mode + ('t' if force_t else '')
open(path, mode)
Defensive patterns

Strategy: validation

Validate before calling

def build_mode(base: str, binary: bool, update: bool = False) -> str:
    assert base in ('r', 'w', 'a', 'x')
    return base + ('b' if binary else 't' if False else '') + ('+' if update else '')
# single `binary` boolean makes t/b mutually exclusive by construction

Type guard

def mode_consistent(mode: str) -> bool:
    s = set(mode)
    return not ('t' in s and 'b' in s)

Prevention

When it happens

Trigger: open('f', 'rtb'), 'wtb', 'r+t+b' — typically a mode assembled programmatically where a default 't' is concatenated onto a user-supplied 'rb'/'wb', or a hand-typed 'rtb'.

Common situations: Mode-building helpers that always append 't' (or default to text) and then receive a base mode that already contains 'b'; merging CLI flags (-t and -b both accepted); documentation examples copied from code that constructed mode strings by concatenation.

Related errors


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