Textualize/rich · error · UnsupportedOperation

writelines

Error message

writelines

What it means

The _Reader object returned by Progress.wrap_file() is a read-only progress-tracking file wrapper. .writelines() is explicitly stubbed to raise io.UnsupportedOperation('writelines') because the class only supports read operations that advance the progress bar; it is not a writable stream.

Source

Thrown at rich/progress.py:282

    def close(self) -> None:
        if self.close_handle:
            self.handle.close()
        self._closed = True

    def seek(self, offset: int, whence: int = 0) -> int:
        pos = self.handle.seek(offset, whence)
        self.progress.update(self.task, completed=pos)
        return pos

    def tell(self) -> int:
        return self.handle.tell()

    def write(self, s: Any) -> int:
        raise UnsupportedOperation("write")

    def writelines(self, lines: Iterable[Any]) -> None:
        raise UnsupportedOperation("writelines")


class _ReadContext(ContextManager[_I], Generic[_I]):
    """A utility class to handle a context for both a reader and a progress."""

    def __init__(self, progress: "Progress", reader: _I) -> None:
        self.progress = progress
        self.reader: _I = reader

    def __enter__(self) -> _I:
        self.progress.start()
        return self.reader.__enter__()

    def __exit__(
        self,
        exc_type: Optional[Type[BaseException]],
        exc_val: Optional[BaseException],
        exc_tb: Optional[TracebackType],

View on GitHub (pinned to 9d8f9a372c)

Solutions

  1. Perform writelines on the underlying raw file object; use the wrapper only for reads.
  2. For write progress, wrap manually: open the file normally, write, then progress.update(task, completed=f.tell()) after each chunk.
  3. Verify the direction of your transfer: wrap_file/Progress.open are for reading (downloads); there is no built-in write wrapper in this API.

Example fix

# before
with progress.open('out.txt', 'rb', total=n) as p:
    p.writelines(lines)  # UnsupportedOperation

# after
with open('out.txt', 'wb') as f:
    f.writelines(lines)
    progress.update(task, completed=f.tell())
Defensive patterns

Strategy: type-guard

Validate before calling

# writelines only on real writable streams
import io
assert isinstance(f, io.IOBase) and f.writable(), 'need a writable raw handle'

Type guard

def is_writable_stream(obj: object) -> bool:
    return isinstance(obj, io.IOBase) and not isinstance(getattr(obj, 'handle', obj), type(None)) and getattr(obj, 'writable', lambda: False)()

Prevention

When it happens

Trigger: Calling wrapped.writelines(lines) on the return value of Progress.wrap_file(...) or Progress.open(...). Commonly hit when the wrapper is passed to a generic serialization routine (csv.writer(...).writerows on a wrapped handle, json.dump into the wrapper) that calls writelines internally.

Common situations: Using Progress.open() while building an output file with a library that writes via writelines; confusing the download-progress wrapper for a read/write stream; copying code that used open() directly into a Progress.open() context.

Related errors


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