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 modeView on GitHub (pinned to 9d8f9a372c)
Solutions
- Remove buffering=0 for text mode: use the default (buffering=-1) or a positive buffer size.
- If you truly need unbuffered reads, open in binary mode: progress.open('f.bin', 'rb', buffering=0).
- 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
- Mirror CPython open() rules: buffering=0 only with binary mode.
- Treat buffering=1 as line-buffering (text) or default buffer (binary, with a warning in rich).
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
- unable to get the total number of bytes, please specify 'tot
- invalid mode {mode!r}
- invalid value for align, expected "left", "center", or "righ
- invalid value for vertical, expected "top", "middle", or "bo
- level must be 'head', 'row' or 'foot'
AI-assisted analysis of Textualize/rich@9d8f9a372c (2026-08-15).
Data as JSON: /api/errors/4801926cefbc03e0.
Report an issue: GitHub.