python/cpython · error · ValueError

can't have read/write/append mode at once

Error message

can't have read/write/append mode at once

What it means

Raised by io.open() as a ValueError when the mode string selects more than one of the base operations create(x)/read(r)/write(w)/append(a). Exactly one base operation is required; '+' adds the complementary direction on top of it.

Source

Thrown at Lib/_pyio.py:219

        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 "") +
                 (writing and "w" or "") +
                 (appending and "a" or "") +

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Use 'r+' for read/write without truncation, 'w+' for read/write with truncation, 'a+' for append+read.
  2. Map boolean flag pairs to complete mode strings via a lookup table instead of concatenating letters.
  3. Reject conflicting config flags (read and write both set without an explicit mode) at config-validation time.

Example fix

// before
mode = ('r' if read else '') + ('w' if write else '')  # 'rw'
open(path, mode)

// after
if read and write:
    mode = 'r+'    # or 'w+' if truncation desired
elif write:
    mode = 'r' if read else 'w'
else:
    mode = 'w' if write else 'r'
# simpler: choose from {'r','w','a','r+','w+','a+'} directly
Defensive patterns

Strategy: validation

Validate before calling

BASE_MODES = {'r','w','a','x','r+','w+','a+','x+','rb','wb','ab','xb','r+b','w+b','a+b','x+b'}

def pick_mode(read: bool, write: bool, binary: bool, truncate: bool) -> str:
    if read and write:
        base = 'w+' if truncate else 'r+'
    elif write:
        base = 'w'
    elif read:
        base = 'r'
    else:
        raise ValueError('need read or write')
    return base + ('b' if binary else '')

Type guard

def one_base_op(mode: str) -> bool:
    return len(set(mode) & {'r','w','a','x'}) == 1

Prevention

When it happens

Trigger: open('f', 'rw') — but note 'rw' usually trips the duplicate-character check (166) first; reachable forms are 'ra', 'rx', 'wa', 'wx', 'ax', and combinations like 'rw+'. Users typically intend 'r+' or 'w+'.

Common situations: Programmatic mode composition that ORs independent read and write booleans into concatenated letters; porting C-style 'rw' fopen modes; config schemas that expose separate read:true and write:true flags mapped naively to mode letters.

Related errors


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