{"record":{"id":"a09006d4412c3f89","repo":"python/cpython","slug":"getstate-on-closed-file","errorCode":null,"errorMessage":"__getstate__ on closed file","messagePattern":"__getstate__ on closed file","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/_pyio.py","lineNumber":898,"sourceCode":"    # Initialize _buffer as soon as possible since it's used by __del__()\n    # which calls close()\n    _buffer = None\n\n    def __init__(self, initial_bytes=None):\n        # Use to keep self._buffer and self._pos consistent.\n        self._lock = Lock()\n\n        buf = bytearray()\n        if initial_bytes is not None:\n            buf += initial_bytes\n\n        with self._lock:\n            self._buffer = buf\n            self._pos = 0\n\n    def __getstate__(self):\n        if self.closed:\n            raise ValueError(\"__getstate__ on closed file\")\n        with self._lock:\n            state = self.__dict__.copy()\n        del state['_lock']\n        return state\n\n    def __setstate__(self, state):\n        self.__dict__.update(state)\n        self._lock = Lock()\n\n    def getvalue(self):\n        \"\"\"Return the bytes value (contents) of the buffer\n        \"\"\"\n        if self.closed:\n            raise ValueError(\"getvalue on closed file\")\n        return bytes(self._buffer)\n\n    def getbuffer(self):\n        \"\"\"Return a readable and writable view of the buffer.","sourceCodeStart":880,"sourceCodeEnd":916,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pyio.py#L880-L916","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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__."],"exampleFix":"# before\nbuf = io.BytesIO(b'payload')\nbuf.write(b'more')\nbuf.close()\npickle.dumps(buf)  # ValueError: __getstate__ on closed file\n\n# after\nbuf = io.BytesIO(b'payload')\nbuf.write(b'more')\npayload = buf.getvalue()  # bytes, freely picklable\nbuf.close()","handlingStrategy":"validation","validationCode":"def pickleable_bytesio(buf: io.BytesIO) -> bytes:\n    if buf.closed:\n        raise ValueError('BytesIO closed; extract getvalue() before close')\n    return buf.getvalue()  # pickle bytes instead of the stream","typeGuard":"def is_open_bytesio(v) -> bool:\n    return isinstance(v, io.BytesIO) and not v.closed","tryCatchPattern":"try:\n    blob = pickle.dumps(buf)\nexcept ValueError as e:\n    if 'closed file' in str(e):\n        raise ValueError('BytesIO was closed before pickling; keep it open or pickle getvalue()') from e\n    raise","preventionTips":["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__."],"tags":["io","bytesio","pickle","lifecycle","valueerror"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}