RustPython/RustPython · error · OSError

seek() returned an invalid position

Error message

seek() returned an invalid position

What it means

Raised by _pyio.BufferedIOBase.seek (Lib/_pyio.py:782) when the wrapped raw stream's seek() returns a negative new position. The buffered layer delegates to raw.seek(pos, whence) and requires the raw object to return the new absolute offset as a non-negative int; a negative return means the underlying stream broke the RawIOBase contract (e.g. a custom subclass returning -1 in C style for failure). It surfaces as a plain OSError with no errno, so it signals a broken raw stream rather than a normal OS-level seek error.

Source

Thrown at Lib/_pyio.py:782

class _BufferedIOMixin(BufferedIOBase):

    """A mixin implementation of BufferedIOBase with an underlying raw stream.

    This passes most requests on to the underlying raw stream.  It
    does *not* provide implementations of read(), readinto() or
    write().
    """

    def __init__(self, raw):
        self._raw = raw

    ### Positioning ###

    def seek(self, pos, whence=0):
        new_position = self.raw.seek(pos, whence)
        if new_position < 0:
            raise OSError("seek() returned an invalid position")
        return new_position

    def tell(self):
        pos = self.raw.tell()
        if pos < 0:
            raise OSError("tell() returned an invalid position")
        return pos

    def truncate(self, pos=None):
        self._checkClosed()
        self._checkWritable()

        # Flush the stream.  We're mixing buffered I/O with lower-level I/O,
        # and a flush may be necessary to synch both views of the current
        # file state.
        self.flush()

        if pos is None:

View on GitHub (pinned to aaeab4f754)

Solutions

  1. Fix the raw stream's seek() to return the new absolute position (>= 0), e.g. return os.lseek(self.fd, pos, whence), instead of -1
  2. Have the custom raw seek() raise OSError itself on failure instead of returning a sentinel
  3. Check raw.seekable() before calling buffered seek() so non-seekable streams are never asked for a position
  4. Add a unit test asserting the custom seek() return value satisfies ret >= 0

Example fix

// before
class MyRaw(io.RawIOBase):
    def seek(self, pos, whence=io.SEEK_SET):
        os.lseek(self.fd, pos, whence)
        return -1  # C-style error sentinel

buf = io.BufferedReader(MyRaw())
buf.seek(0)  # OSError: seek() returned an invalid position

// after
class MyRaw(io.RawIOBase):
    def seek(self, pos, whence=io.SEEK_SET):
        return os.lseek(self.fd, pos, whence)

buf.seek(0)
Defensive patterns

Strategy: try-catch

Validate before calling

pos = raw.seek(0, io.SEEK_CUR)
if not isinstance(pos, int) or pos < 0:
    raise RuntimeError("raw stream seek() contract violated: %r" % (pos,))

Type guard

def valid_raw_seek_result(ret) -> bool:
    return isinstance(ret, int) and ret >= 0

Try / catch

try:
    f.seek(offset, whence)
except OSError as e:
    if e.args == ("seek() returned an invalid position",):
        # raw stream broke the seek() contract — fix its implementation
        raise
    raise

Prevention

When it happens

Trigger: Calling seek() on any buffered wrapper (open(path,'rb'), BufferedReader, BufferedWriter) whose raw layer is a user-defined io.RawIOBase subclass whose seek() returns -1; adapters over sockets/pipes or custom block devices that fake seekability; test mocks where seek is stubbed as lambda p, w: -1.

Common situations: Porting C code where lseek-style helpers return -1 on error; wrapping non-seekable transports (network streams, compressed sources) in a class that claims to seek; unit tests with over-simplified raw-stream doubles; third-party storage adapters implementing RawIOBase incorrectly.

Related errors


AI-assisted analysis of RustPython/RustPython@aaeab4f754 (2026-08-17). Data as JSON: /api/errors/318fae7eec63128d. Report an issue: GitHub.