{"record":{"id":"78c732d704af8b58","repo":"python/cpython","slug":"peek-on-closed-file","errorCode":null,"errorMessage":"peek on closed file","messagePattern":"peek on closed file","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/_pyio.py","lineNumber":1005,"sourceCode":"            self._pos = pos\n        elif whence == 1:\n            with self._lock:\n                self._pos = max(0, self._pos + pos)\n        elif whence == 2:\n            with self._lock:\n                self._pos = max(0, len(self._buffer) + pos)\n        else:\n            raise ValueError(\"unsupported whence value\")\n        return self._pos\n\n    def tell(self):\n        if self.closed:\n            raise ValueError(\"tell on closed file\")\n        return self._pos\n\n    def peek(self, size=0):\n        if self.closed:\n            raise ValueError(\"peek on closed file\")\n        if size < 1:\n            return self._buffer[self._pos:self._pos + io.DEFAULT_BUFFER_SIZE]\n        return self._buffer[self._pos:self._pos + size]\n\n    def truncate(self, pos=None):\n        if self.closed:\n            raise ValueError(\"truncate on closed file\")\n\n        with self._lock:\n            if pos is None:\n                pos = self._pos\n            else:\n                try:\n                    pos_index = pos.__index__\n                except AttributeError:\n                    raise TypeError(f\"{pos!r} is not an integer\")\n                else:\n                    pos = pos_index()","sourceCodeStart":987,"sourceCodeEnd":1023,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pyio.py#L987-L1023","documentation":"Raised by BytesIO.peek() in Python's pure-Python io module when the stream has already been closed. peek() tries to look ahead into the in-memory buffer without consuming it, but a closed BytesIO has been released and no I/O is permitted. It is a ValueError raised eagerly before any buffer slicing occurs.","triggerScenarios":"Calling bio.peek() (optionally with a size) on a BytesIO instance after bio.close() was called, e.g. peeking during cleanup code or after a with block exited.","commonSituations":"Peeking inside a 'with io.BytesIO(...) as f:' block after the block exited; reusing a BytesIO stored on an object whose close() lifecycle already ran; test teardown code that closes fixtures then inspects them.","solutions":["Ensure peek() is called before close() / before the 'with' block exits.","Guard the call with 'if not bio.closed:' before peeking.","If late inspection is intended, keep the raw bytes separately (e.g. getvalue() before closing) instead of peeking a closed stream."],"exampleFix":"// before\nbuf = io.BytesIO(b\"data\")\nbuf.close()\nbuf.peek(2)  # ValueError\n\n// after\nbuf = io.BytesIO(b\"data\")\nhead = buf.peek(2)\nbuf.close()","handlingStrategy":"validation","validationCode":"if not buf.closed:\n    head = buf.peek(4)\nelse:\n    head = b\"\"  # stream finished","typeGuard":null,"tryCatchPattern":"try:\n    head = buf.peek(4)\nexcept ValueError as e:\n    if \"closed\" in str(e):\n        raise RuntimeError(\"stream closed before peek\") from e\n    raise","preventionTips":["Extract data with getvalue() before closing when later inspection is needed.","Keep all reads/peeks inside the with-block that owns the BytesIO.","Centralize close() in one owner so consumers never race it."],"tags":["python","io","bytesio","closed-file","valueerror"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}