python/cpython · error · ValueError

negative seek position %r

Error message

negative seek position %r

What it means

BytesIO.seek (Lib/_pyio.py:986) raises ValueError('negative seek position') when whence is 0 (SEEK_SET, the default) and pos is negative. An absolute position cannot be below zero, so instead of clamping or wrapping around, BytesIO rejects it outright. Note the whence==1 and whence==2 branches clamp with max(0, ...) — only absolute seeks are strict.

Source

Thrown at Lib/_pyio.py:986

                if pos > len(self._buffer):
                    # Pad buffer to pos with null bytes.
                    self._buffer.resize(pos)
                self._buffer[pos:pos + n] = view
                self._pos += n
            return n

    def seek(self, pos, whence=0):
        if self.closed:
            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:

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. For positions relative to the end, pass the whence explicitly: `buf.seek(-4, io.SEEK_END)`.
  2. For relative moves use `buf.seek(-4, io.SEEK_CUR)`.
  3. Validate computed absolute offsets: `pos = max(0, computed)` only if clamping is intended, otherwise treat negative values as a parsing error.

Example fix

# before
buf.seek(-4)  # absolute negative -> ValueError

# after
buf.seek(-4, io.SEEK_END)  # 4 bytes before end, as intended
Defensive patterns

Strategy: validation

Validate before calling

import io
def seek_absolute(buf: io.BytesIO, pos: int):
    if pos < 0:
        raise ValueError(f'absolute seek position must be >= 0, got {pos}')
    buf.seek(pos, io.SEEK_SET)

Try / catch

try:
    buf.seek(pos)
except ValueError as e:
    if 'negative seek position' in str(e):
        buf.seek(0)  # or raise a parse error: negative absolute offset is a logic bug
    else:
        raise

Prevention

When it happens

Trigger: buf.seek(-4) or buf.seek(-4, io.SEEK_SET); passing a negative offset computed from a header that turns out larger than expected; reusing socket-style negative offsets (meaning 'relative to end') with the default whence.

Common situations: Mixing up whence conventions: expecting seek(-n) to be relative (it is absolute by default); unsigned-length underflow from struct unpacking; backtracking computed as start - consumed where consumed > start.

Related errors


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