python/cpython · error · ValueError

read from closed file

Error message

read from closed file

What it means

BytesIO.read (Lib/_pyio.py:929) checks the closed flag before touching the (already-released) internal buffer and raises ValueError('read from closed file'). Because close() empties the bytearray, reading after close could silently return b'' — the guard converts that silent data loss into an explicit error.

Source

Thrown at Lib/_pyio.py:929

        if self.closed:
            raise ValueError("getvalue on closed file")
        return bytes(self._buffer)

    def getbuffer(self):
        """Return a readable and writable view of the buffer.
        """
        if self.closed:
            raise ValueError("getbuffer on closed file")
        return memoryview(self._buffer)

    def close(self):
        if self._buffer is not None:
            self._buffer = bytearray()
        super().close()

    def read(self, size=-1):
        if self.closed:
            raise ValueError("read from closed file")
        if size is None:
            size = -1
        else:
            try:
                size_index = size.__index__
            except AttributeError:
                raise TypeError(f"{size!r} is not an integer")
            else:
                size = size_index()

        with self._lock:
            if size < 0:
                size = len(self._buffer)
            if len(self._buffer) <= self._pos:
                return b""
            newpos = min(len(self._buffer), self._pos + size)
            b = self._buffer[self._pos : newpos]
            self._pos = newpos

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Remove or relocate the close() so it runs after the final read.
  2. Use `with io.BytesIO(...) as buf:` and keep every read inside the block.
  3. Guard with `if not buf.closed:` when ownership of the stream is shared across functions.

Example fix

# before
buf = io.BytesIO(data)
first = buf.read(4)
buf.close()
rest = buf.read()  # ValueError: read from closed file

# after
buf = io.BytesIO(data)
first = buf.read(4)
rest = buf.read()   # all reads first
buf.close()         # close last (or omit)
Defensive patterns

Strategy: validation

Validate before calling

def read_if_open(buf: io.BytesIO, n: int = -1) -> bytes:
    if buf.closed:
        raise ValueError('BytesIO closed; reads must happen before close')
    return buf.read(n)

Type guard

def is_open_bytesio(v) -> bool:
    return isinstance(v, io.BytesIO) and not v.closed

Try / catch

try:
    chunk = buf.read(n)
except ValueError as e:
    if 'closed file' in str(e):
        raise RuntimeError('consumer raced with producer close') from e
    raise

Prevention

When it happens

Trigger: buf.read() / buf.read(n) after buf.close(); calling read() on a BytesIO returned from a `with` block after exit; a consumer reading a stream that a producer's error handler already closed.

Common situations: Response-body assembly where cleanup closes the buffer before the sender reads it; chaining processors where each stage closes its input; helper that closes on EOF while the caller loops once more.

Related errors


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