python/cpython · error · OSError

seek() returned invalid position

Error message

seek() returned invalid position

What it means

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.

Source

Thrown at Lib/_pyio.py:1451

        raw._checkSeekable()
        BufferedReader.__init__(self, raw, buffer_size)
        BufferedWriter.__init__(self, raw, buffer_size)

    def seek(self, pos, whence=0):
        if whence not in valid_seek_flags:
            raise ValueError("invalid whence value")
        self.flush()
        if self._read_buf:
            # Undo read ahead.
            with self._read_lock:
                self.raw.seek(self._read_pos - len(self._read_buf), 1)
        # First do the raw seek, then empty the read buffer, so that
        # if the raw seek fails, we don't lose buffered data forever.
        pos = self.raw.seek(pos, whence)
        with self._read_lock:
            self._reset_read_buf()
        if pos < 0:
            raise OSError("seek() returned invalid position")
        return pos

    def tell(self):
        if self._write_buf:
            return BufferedWriter.tell(self)
        else:
            return BufferedReader.tell(self)

    def truncate(self, pos=None):
        if pos is None:
            pos = self.tell()
        # Use seek to flush the read buffer.
        return BufferedWriter.truncate(self, pos)

    def read(self, size=None):
        if size is None:
            size = -1
        self.flush()

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Fix the underlying raw object: seek() must return the new non-negative offset or raise OSError, never a negative number
  2. In tests, make mock raw streams return a valid int (e.g. the requested offset)
  3. If you cannot fix the raw class, wrap it and clamp/convert negative returns into an OSError

Example fix

# before
class MyRaw(io.RawIOBase):
    def seek(self, pos, whence=0):
        return -1  # signals failure -> OSError: seek() returned invalid position

# after
class MyRaw(io.RawIOBase):
    def seek(self, pos, whence=0):
        new = self._do_seek(pos, whence)
        if new < 0:
            raise OSError('seek failed')
        return new
Defensive patterns

Strategy: try-catch

Try / catch

try:
    new_pos = f.seek(pos, whence)
except OSError as e:
    if 'invalid position' in str(e):
        # raw layer misbehaved; reopen or resync the stream
        pos_now = f.raw.seek(0, os.SEEK_CUR)
        raise RuntimeError(f'raw seek broken, current={pos_now}') from e
    raise

Prevention

When it happens

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

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

Related errors


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