{"record":{"id":"4801926cefbc03e0","repo":"Textualize/rich","slug":"can-t-have-unbuffered-text-i-o","errorCode":null,"errorMessage":"can't have unbuffered text I/O","messagePattern":"can't have unbuffered text I/O","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"rich/progress.py","lineNumber":1358,"sourceCode":"        Raises:\n            ValueError: When an invalid mode is given.\n        \"\"\"\n        # normalize the mode (always rb, rt)\n        _mode = \"\".join(sorted(mode, reverse=False))\n        if _mode not in (\"br\", \"rt\", \"r\"):\n            raise ValueError(f\"invalid mode {mode!r}\")\n\n        # patch buffering to provide the same behaviour as the builtin `open`\n        line_buffering = buffering == 1\n        if _mode == \"br\" and buffering == 1:\n            warnings.warn(\n                \"line buffering (buffering=1) isn't supported in binary mode, the default buffer size will be used\",\n                RuntimeWarning,\n            )\n            buffering = -1\n        elif _mode in (\"rt\", \"r\"):\n            if buffering == 0:\n                raise ValueError(\"can't have unbuffered text I/O\")\n            elif buffering == 1:\n                buffering = -1\n\n        # attempt to get the total with `os.stat`\n        if total is None:\n            total = stat(file).st_size\n\n        # update total of task or create new task\n        if task_id is None:\n            task_id = self.add_task(description, total=total)\n        else:\n            self.update(task_id, total=total)\n\n        # open the file in binary mode,\n        handle = io.open(file, \"rb\", buffering=buffering)\n        reader = _Reader(handle, self, task_id, close_handle=True)\n\n        # wrap the reader in a `TextIOWrapper` if text mode","sourceCodeStart":1340,"sourceCodeEnd":1376,"githubUrl":"https://github.com/Textualize/rich/blob/9d8f9a372cc5916fd4781fec207ced7ddac2f08f/rich/progress.py#L1340-L1376","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nwith progress.open('f.txt', 'r', buffering=0) as f:  # ValueError\n    ...\n\n# after\nwith progress.open('f.txt', 'r') as f:  # default buffering\n    ...","handlingStrategy":"validation","validationCode":"if mode in ('r', 'rt') and buffering == 0:\n    raise ValueError('use binary mode for unbuffered I/O')\n# then call progress.open(path, mode, buffering=buffering)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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)."],"tags":["rich","progress","buffering","text-io","valueerror"],"backgroundTag":null,"analyzedSha":"9d8f9a372cc5916fd4781fec207ced7ddac2f08f","analyzedAt":"2026-08-15T03:30:11.781Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}