Textualize/rich · error · ValueError

can't have unbuffered text I/O

Error message

can't have unbuffered text I/O

What it means

Progress.open() mimics the builtin open()'s buffering rules. Text mode ('r'/'rt') with buffering=0 raises ValueError('can\'t have unbuffered text I/O') — exactly as CPython does — because unbuffered text streams are not supported by io.TextIOWrapper, which rich uses under the hood.

Source

Thrown at rich/progress.py:1358

        Raises:
            ValueError: When an invalid mode is given.
        """
        # normalize the mode (always rb, rt)
        _mode = "".join(sorted(mode, reverse=False))
        if _mode not in ("br", "rt", "r"):
            raise ValueError(f"invalid mode {mode!r}")

        # patch buffering to provide the same behaviour as the builtin `open`
        line_buffering = buffering == 1
        if _mode == "br" and buffering == 1:
            warnings.warn(
                "line buffering (buffering=1) isn't supported in binary mode, the default buffer size will be used",
                RuntimeWarning,
            )
            buffering = -1
        elif _mode in ("rt", "r"):
            if buffering == 0:
                raise ValueError("can't have unbuffered text I/O")
            elif buffering == 1:
                buffering = -1

        # attempt to get the total with `os.stat`
        if total is None:
            total = stat(file).st_size

        # update total of task or create new task
        if task_id is None:
            task_id = self.add_task(description, total=total)
        else:
            self.update(task_id, total=total)

        # open the file in binary mode,
        handle = io.open(file, "rb", buffering=buffering)
        reader = _Reader(handle, self, task_id, close_handle=True)

        # wrap the reader in a `TextIOWrapper` if text mode

View on GitHub (pinned to 9d8f9a372c)

Solutions

  1. Remove buffering=0 for text mode: use the default (buffering=-1) or a positive buffer size.
  2. If you truly need unbuffered reads, open in binary mode: progress.open('f.bin', 'rb', buffering=0).
  3. Wrap manually: read binary unbuffered and decode yourself if you need byte-level control plus text.

Example fix

# before
with progress.open('f.txt', 'r', buffering=0) as f:  # ValueError
    ...

# after
with progress.open('f.txt', 'r') as f:  # default buffering
    ...
Defensive patterns

Strategy: validation

Validate before calling

if mode in ('r', 'rt') and buffering == 0:
    raise ValueError('use binary mode for unbuffered I/O')
# then call progress.open(path, mode, buffering=buffering)

Prevention

When it happens

Trigger: progress.open('f.txt', 'r', buffering=0); also mode 'rt' with buffering=0. Note the sibling behavior: buffering=1 in text mode is silently coerced to the default buffer size, and in binary mode buffering=1 only emits a RuntimeWarning.

Common situations: Porting code that used open(..., buffering=0) for performance on binary files to text mode via Progress.open; assuming rich relaxes CPython's rules; a buffering value flowing in from configuration that was tuned for binary I/O.

Related errors


AI-assisted analysis of Textualize/rich@9d8f9a372c (2026-08-15). Data as JSON: /api/errors/4801926cefbc03e0. Report an issue: GitHub.