RustPython/RustPython · error · UnsupportedOperation

File or stream is not seekable.

Error message

File or stream is not seekable.

What it means

io.UnsupportedOperation raised through IOBase._checkSeekable when a positioning operation (seek, tell, truncate) is attempted on a stream whose seekable() returns False. Seekability is a property of the underlying channel - pipes, sockets, and some wrapped streams are not seekable, while regular files and BytesIO are. The optional msg parameter lets subclasses customize the text; the default message is shown here.

Source

Thrown at Lib/_pyio.py:434

        # If close() fails, the caller logs the exception with
        # sys.unraisablehook. close() must be called at the end at __del__().
        self.close()

    ### Inquiries ###

    def seekable(self):
        """Return a bool indicating whether object supports random access.

        If False, seek(), tell() and truncate() will raise OSError.
        This method may need to do a test seek().
        """
        return False

    def _checkSeekable(self, msg=None):
        """Internal: raise UnsupportedOperation if file is not seekable
        """
        if not self.seekable():
            raise UnsupportedOperation("File or stream is not seekable."
                                       if msg is None else msg)

    def readable(self):
        """Return a bool indicating whether object was opened for reading.

        If False, read() will raise OSError.
        """
        return False

    def _checkReadable(self, msg=None):
        """Internal: raise UnsupportedOperation if file is not readable
        """
        if not self.readable():
            raise UnsupportedOperation("File or stream is not readable."
                                       if msg is None else msg)

    def writable(self):
        """Return a bool indicating whether object was opened for writing.

View on GitHub (pinned to aaeab4f754)

Solutions

  1. Branch on the capability: if f.seekable(): f.seek(0) else re-read from the source
  2. Buffer the stream first: data = f.read(), then work on io.BytesIO(data), which is seekable
  3. For sockets/pipes, design a streaming parse that never rewinds

Example fix

# before
f.seek(0)                    # dies when f is a pipe
header = f.read(16)

# after
from io import BytesIO
data = f.read()
buf = BytesIO(data)          # seekable in-memory copy
header = buf.read(16)
Defensive patterns

Strategy: validation

Validate before calling

if f.seekable():
    f.seek(0)
    header = f.read(16)
else:                                    # pipe/socket: buffer it instead
    data = f.read()
    header = data[:16]

Type guard

def is_seekable(f) -> bool:
    return bool(getattr(f, 'seekable', lambda: False)())

Try / catch

import io
try:
    f.seek(0)
except io.UnsupportedOperation:
    data = f.read()      # stream once, buffer locally

Prevention

When it happens

Trigger: f.seek(0) on sys.stdin/sys.stdout when they are pipes; seek()/tell() on a socket file object from sock.makefile(); seeking a subprocess stdout pipe; a custom stream subclass that left seekable() returning the IOBase default False.

Common situations: Code tested against real files but run with piped stdin (curl ... | script); seek(0) to rewind for a second parse pass; serialization helpers that tell() to record offsets; multipart parsers that need lookahead.

Related errors


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