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
- 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
- Have the custom raw seek() raise OSError itself on failure instead of returning a sentinel
- Check raw.seekable() before calling buffered seek() so non-seekable streams are never asked for a position
- 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
- Raw seek() implementations must return the new absolute offset, never -1
- Raise OSError inside the raw stream on real failures instead of returning sentinels
- Unit-test custom RawIOBase.seek with self.assertGreaterEqual(ret, 0)
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
- tell() returned an invalid position
- seek() returned invalid position
- raw stream already detached
- seek on closed file
- {pos!r} is not an integer
AI-assisted analysis of RustPython/RustPython@aaeab4f754 (2026-08-17).
Data as JSON: /api/errors/318fae7eec63128d.
Report an issue: GitHub.