{"record":{"id":"0079059b380189b9","repo":"python/cpython","slug":"unsupported-whence-value","errorCode":null,"errorMessage":"unsupported whence value","messagePattern":"unsupported whence value","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/_pyio.py","lineNumber":995,"sourceCode":"            raise ValueError(\"seek on closed file\")\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()\n        if whence == 0:\n            if pos < 0:\n                raise ValueError(\"negative seek position %r\" % (pos,))\n            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","sourceCodeStart":977,"sourceCodeEnd":1013,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pyio.py#L977-L1013","documentation":"BytesIO.seek (Lib/_pyio.py:995) accepts exactly three whence values — 0 (SEEK_SET), 1 (SEEK_CUR), 2 (SEEK_END) — and raises ValueError('unsupported whence value') for anything else. The check happens after all position arithmetic branches fail, catching typos, out-of-range constants, and mistaken API conventions.","triggerScenarios":"buf.seek(0, 3) or buf.seek(0, -1); passing an unmapped enum/int constant (e.g. a custom SEEK-specific value or os.SEEK_HOLE/SEEK_DATA unsupported here); forwarding a whence from user input without validation.","commonSituations":"Confusing this API with C's lseek extensions (SEEK_DATA/SEEK_HOLE on Solaris/Linux); typos like whence=1 vs intended 2; protocol fields encoding whence as arbitrary ints.","solutions":["Use the named constants io.SEEK_SET / io.SEEK_CUR / io.SEEK_END (or os.SEEK_*) instead of raw ints.","Validate whence at the boundary: `if whence not in (0, 1, 2): raise ValueError(...)` with your own message.","Map any custom seek semantics to one of the three supported modes before calling seek()."],"exampleFix":"# before\nbuf.seek(offset, whence=3)  # typo / unsupported -> ValueError\n\n# after\nimport io\nbuf.seek(offset, io.SEEK_END)  # named constant, self-documenting","handlingStrategy":"validation","validationCode":"import io\nVALID_WHENCE = (io.SEEK_SET, io.SEEK_CUR, io.SEEK_END)\nif whence not in VALID_WHENCE:\n    raise ValueError(f'whence must be one of {VALID_WHENCE}, got {whence!r}')\nbuf.seek(pos, whence)","typeGuard":null,"tryCatchPattern":"try:\n    buf.seek(pos, whence)\nexcept ValueError as e:\n    if 'unsupported whence' in str(e):\n        buf.seek(pos, io.SEEK_SET)  # fall back to absolute with a logged warning\n    else:\n        raise","preventionTips":["Always pass io.SEEK_SET / io.SEEK_CUR / io.SEEK_END instead of raw ints.","Validate whence fields from protocols/user input against (0, 1, 2) at the boundary.","Do not assume lseek extensions (SEEK_DATA, SEEK_HOLE) exist in Python's io layer."],"tags":["io","bytesio","seek","whence","valueerror"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}