python/cpython · error · ValueError

unsupported whence value

Error message

unsupported whence value

What it means

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.

Source

Thrown at Lib/_pyio.py:995

            raise ValueError("seek on closed file")
        try:
            pos_index = pos.__index__
        except AttributeError:
            raise TypeError(f"{pos!r} is not an integer")
        else:
            pos = pos_index()
        if whence == 0:
            if pos < 0:
                raise ValueError("negative seek position %r" % (pos,))
            self._pos = pos
        elif whence == 1:
            with self._lock:
                self._pos = max(0, self._pos + pos)
        elif whence == 2:
            with self._lock:
                self._pos = max(0, len(self._buffer) + pos)
        else:
            raise ValueError("unsupported whence value")
        return self._pos

    def tell(self):
        if self.closed:
            raise ValueError("tell on closed file")
        return self._pos

    def peek(self, size=0):
        if self.closed:
            raise ValueError("peek on closed file")
        if size < 1:
            return self._buffer[self._pos:self._pos + io.DEFAULT_BUFFER_SIZE]
        return self._buffer[self._pos:self._pos + size]

    def truncate(self, pos=None):
        if self.closed:
            raise ValueError("truncate on closed file")

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Use the named constants io.SEEK_SET / io.SEEK_CUR / io.SEEK_END (or os.SEEK_*) instead of raw ints.
  2. Validate whence at the boundary: `if whence not in (0, 1, 2): raise ValueError(...)` with your own message.
  3. Map any custom seek semantics to one of the three supported modes before calling seek().

Example fix

# before
buf.seek(offset, whence=3)  # typo / unsupported -> ValueError

# after
import io
buf.seek(offset, io.SEEK_END)  # named constant, self-documenting
Defensive patterns

Strategy: validation

Validate before calling

import io
VALID_WHENCE = (io.SEEK_SET, io.SEEK_CUR, io.SEEK_END)
if whence not in VALID_WHENCE:
    raise ValueError(f'whence must be one of {VALID_WHENCE}, got {whence!r}')
buf.seek(pos, whence)

Try / catch

try:
    buf.seek(pos, whence)
except ValueError as e:
    if 'unsupported whence' in str(e):
        buf.seek(pos, io.SEEK_SET)  # fall back to absolute with a logged warning
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/0079059b380189b9. Report an issue: GitHub.