python/cpython · error · UnsupportedOperation

File or stream is not seekable.

Error message

File or stream is not seekable.

What it means

Raised by IOBase._checkSeekable() as io.UnsupportedOperation when seek(), tell(), or truncate() is called on a stream whose seekable() returns False. Non-seekable streams are sequential-only OS channels — pipes, sockets, terminal input — where a position does not exist, so random access is refused.

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 bc6749cc3b)

Solutions

  1. Guard with if stream.seekable(): before seek/tell/truncate.
  2. For stdin re-reading, buffer the content first: data = sys.stdin.buffer.read(), then parse from memory (io.BytesIO(data) is seekable).
  3. Restructure one-pass: single-pass parsing instead of seek-back rereads.
  4. Catch io.UnsupportedOperation where stream provenance varies (file vs pipe).

Example fix

// before
sys.stdin.seek(0)          # UnsupportedOperation on a pipe
lines2 = sys.stdin.readlines()

// after
data = sys.stdin.buffer.read()      # one full pass
import io
stream = io.BytesIO(data)          # seekable in-memory copy
lines2 = stream.readlines()
Defensive patterns

Strategy: type-guard

Validate before calling

if stream.seekable():
    stream.seek(0)
    payload = stream.read()
else:
    payload = stream.read()          # single pass; no rewind possible

Type guard

def is_seekable(stream) -> bool:
    probe = getattr(stream, 'seekable', None)
    return callable(probe) and probe()

Try / catch

import io

try:
    stream.seek(0)
except io.UnsupportedOperation:
    stream = io.BytesIO(original_bytes)   # fallback: seekable in-memory copy

Prevention

When it happens

Trigger: sys.stdin.seek(0); f.seek(0) where f wraps a pipe (e.g. output of subprocess.Popen(..., stdout=PIPE)); gzip/bz2 modules or fileinput probing the underlying stream; calling .tell() on a socket makefile() object; code that re-reads input by seeking back to position 0.

Common situations: Scripts that work on regular files and then get a pipe redirected in (cmd | script) — the seek(0)-to-reread pattern breaks; stdin re-reading in interactive tools; feeding subprocess pipes into parsers that expect random access; HTTP chunked uploads trying to seek a streamed body.

Related errors


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