python/cpython · error · ValueError

write to closed file

Error message

write to closed file

What it means

BytesIO.write (Lib/_pyio.py:960) checks the closed flag inside the memoryview context before mutating the buffer and raises ValueError('write to closed file'). After close() the internal bytearray has been swapped for an empty one, so a late write would either fail confusingly or appear to lose data — the check makes the state explicit.

Source

Thrown at Lib/_pyio.py:960

                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
            return b.take_bytes()

    def read1(self, size=-1):
        """This is the same as read.
        """
        return self.read(size)

    def write(self, b):
        if isinstance(b, str):
            raise TypeError("can't write str to binary stream")
        with memoryview(b) as view:
            if self.closed:
                raise ValueError("write to closed file")

            n = view.nbytes  # Size of any bytes-like object
            if n == 0:
                return 0

            with self._lock:
                pos = self._pos
                if pos > len(self._buffer):
                    # Pad buffer to pos with null bytes.
                    self._buffer.resize(pos)
                self._buffer[pos:pos + n] = view
                self._pos += n
            return n

    def seek(self, pos, whence=0):
        if self.closed:
            raise ValueError("seek on closed file")
        try:

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Ensure close() runs only after the last write; reorder cleanup to the very end.
  2. Guard writes on shared streams: `if not buf.closed: buf.write(chunk)`.
  3. On cancellation/error paths, drain or finalize pending writes before closing the buffer.

Example fix

# before
buf = io.BytesIO()
try:
    for chunk in source():
        buf.write(chunk)
except ValueError:
    buf.close()
    buf.write(b'partial')  # ValueError: write to closed file

# after
buf = io.BytesIO()
try:
    for chunk in source():
        buf.write(chunk)
except ValueError:
    pass  # keep buf open
finally:
    result = buf.getvalue()
    buf.close()  # close once, at the end
Defensive patterns

Strategy: validation

Validate before calling

def write_if_open(buf: io.BytesIO, chunk: bytes) -> int:
    if buf.closed:
        raise ValueError('BytesIO closed; writes must happen before close')
    return buf.write(chunk)

Type guard

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

Try / catch

try:
    buf.write(chunk)
except ValueError as e:
    if 'closed file' in str(e):
        raise RuntimeError('writer ran after buffer close; reorder cleanup') from e
    raise

Prevention

When it happens

Trigger: buf.write(b'...') after buf.close(); writing inside a loop where an earlier iteration's error handler closed the stream; a `with io.BytesIO()` block exited before a deferred/batched write executes.

Common situations: Buffered writers that accumulate output and flush/close on error, with later code still writing; async tasks writing to a shared BytesIO closed by cancellation; generator pipelines where close propagates downstream early.

Related errors


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