{"record":{"id":"54a3ec675877d6da","repo":"python/cpython","slug":"seek-returned-invalid-position","errorCode":null,"errorMessage":"seek() returned invalid position","messagePattern":"seek\\(\\) returned invalid position","errorType":"exception","errorClass":"OSError","httpStatus":null,"severity":"error","filePath":"Lib/_pyio.py","lineNumber":1451,"sourceCode":"        raw._checkSeekable()\n        BufferedReader.__init__(self, raw, buffer_size)\n        BufferedWriter.__init__(self, raw, buffer_size)\n\n    def seek(self, pos, whence=0):\n        if whence not in valid_seek_flags:\n            raise ValueError(\"invalid whence value\")\n        self.flush()\n        if self._read_buf:\n            # Undo read ahead.\n            with self._read_lock:\n                self.raw.seek(self._read_pos - len(self._read_buf), 1)\n        # First do the raw seek, then empty the read buffer, so that\n        # if the raw seek fails, we don't lose buffered data forever.\n        pos = self.raw.seek(pos, whence)\n        with self._read_lock:\n            self._reset_read_buf()\n        if pos < 0:\n            raise OSError(\"seek() returned invalid position\")\n        return pos\n\n    def tell(self):\n        if self._write_buf:\n            return BufferedWriter.tell(self)\n        else:\n            return BufferedReader.tell(self)\n\n    def truncate(self, pos=None):\n        if pos is None:\n            pos = self.tell()\n        # Use seek to flush the read buffer.\n        return BufferedWriter.truncate(self, pos)\n\n    def read(self, size=None):\n        if size is None:\n            size = -1\n        self.flush()","sourceCodeStart":1433,"sourceCodeEnd":1469,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pyio.py#L1433-L1469","documentation":"Raised by BufferedRandom.seek() when the underlying raw stream's seek() returns a negative position. The code deliberately performs the raw seek first and only then resets the read buffer, so a negative return indicates the raw object violated the IO protocol (positions must be non-negative); the buffer state is not the cause but the trigger is the raw layer.","triggerScenarios":"A custom RawIOBase-like object whose seek() returns -1 or another negative value on failure instead of raising OSError; mocked raw streams in tests stubbed to return -1; a raw wrapper around an exotic device where a negative result leaks through from the OS layer without being converted to an exception.","commonSituations":"Writing custom file-like objects (protocol implementations for SFTP/blob storage) that copy C conventions of returning -1 on error; test doubles with incorrect seek return values; partially-implemented adapters between file APIs.","solutions":["Fix the underlying raw object: seek() must return the new non-negative offset or raise OSError, never a negative number","In tests, make mock raw streams return a valid int (e.g. the requested offset)","If you cannot fix the raw class, wrap it and clamp/convert negative returns into an OSError"],"exampleFix":"# before\nclass MyRaw(io.RawIOBase):\n    def seek(self, pos, whence=0):\n        return -1  # signals failure -> OSError: seek() returned invalid position\n\n# after\nclass MyRaw(io.RawIOBase):\n    def seek(self, pos, whence=0):\n        new = self._do_seek(pos, whence)\n        if new < 0:\n            raise OSError('seek failed')\n        return new","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    new_pos = f.seek(pos, whence)\nexcept OSError as e:\n    if 'invalid position' in str(e):\n        # raw layer misbehaved; reopen or resync the stream\n        pos_now = f.raw.seek(0, os.SEEK_CUR)\n        raise RuntimeError(f'raw seek broken, current={pos_now}') from e\n    raise","preventionTips":["Custom raw seek() must return a non-negative int or raise OSError — never -1","Property-test custom file objects against io.BufferedRandom to catch protocol violations","In mocks, return the requested offset from seek stubs"],"tags":["python","io","seek","custom-file-object","oserror"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}