python/cpython · error · ValueError
__getstate__ on closed file
Error message
__getstate__ on closed file
What it means
Unlike raw file objects, BytesIO is picklable, but its __getstate__ (Lib/_pyio.py:898) refuses to serialize when the stream is closed, raising ValueError('__getstate__ on closed file'). A closed BytesIO has had its buffer released to an empty bytearray, so pickling it would silently produce an empty stream — the guard makes that data loss explicit.
Source
Thrown at Lib/_pyio.py:898
# Initialize _buffer as soon as possible since it's used by __del__()
# which calls close()
_buffer = None
def __init__(self, initial_bytes=None):
# Use to keep self._buffer and self._pos consistent.
self._lock = Lock()
buf = bytearray()
if initial_bytes is not None:
buf += initial_bytes
with self._lock:
self._buffer = buf
self._pos = 0
def __getstate__(self):
if self.closed:
raise ValueError("__getstate__ on closed file")
with self._lock:
state = self.__dict__.copy()
del state['_lock']
return state
def __setstate__(self, state):
self.__dict__.update(state)
self._lock = Lock()
def getvalue(self):
"""Return the bytes value (contents) of the buffer
"""
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.View on GitHub (pinned to bc6749cc3b)
Solutions
- Extract the value before closing and pickle that: `payload = buf.getvalue(); buf.close()`.
- Drop the explicit close() on BytesIO — it is pure memory and garbage collection reclaims it without close.
- If BytesIO lives in a larger picklable object, convert it to bytes in that object's __getstate__.
Example fix
# before buf = io.BytesIO(b'payload') buf.write(b'more') buf.close() pickle.dumps(buf) # ValueError: __getstate__ on closed file # after buf = io.BytesIO(b'payload') buf.write(b'more') payload = buf.getvalue() # bytes, freely picklable buf.close()
Defensive patterns
Strategy: validation
Validate before calling
def pickleable_bytesio(buf: io.BytesIO) -> bytes:
if buf.closed:
raise ValueError('BytesIO closed; extract getvalue() before close')
return buf.getvalue() # pickle bytes instead of the stream Type guard
def is_open_bytesio(v) -> bool:
return isinstance(v, io.BytesIO) and not v.closed Try / catch
try:
blob = pickle.dumps(buf)
except ValueError as e:
if 'closed file' in str(e):
raise ValueError('BytesIO was closed before pickling; keep it open or pickle getvalue()') from e
raise Prevention
- Pickling the bytes payload (getvalue()) rather than the BytesIO object.
- Do not explicitly close BytesIO before serialization — or at all.
- Keep serialized containers free of stream members; convert them in __getstate__.
When it happens
Trigger: pickle.dumps(buf) or copy.deepcopy(buf) on a BytesIO after buf.close(); a request handler closes a BytesIO used for response assembly, then the framework attempts to pickle/cache it.
Common situations: Caching layers (beaker, job queues) that pickle payload objects containing BytesIO members closed by earlier cleanup; explicitly closing BytesIO for tidiness in memory-conscious code before serialization; multiprocessing pipelines over BytesIO chunks.
Related errors
- getvalue on closed file
- getbuffer on closed file
- read from closed file
- write to closed file
- seek on closed file
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/a09006d4412c3f89.
Report an issue: GitHub.