python/cpython · error · OSError

"reader" argument must be readable.

Error message

"reader" argument must be readable.

What it means

Raised by io.BufferedRWPair.__init__ (pure-Python _pyio variant) when the object passed as reader does not report itself as readable via its readable() method. BufferedRWPair combines two raw IO objects into one buffered read/write pair, and it verifies up front that the reader side actually supports reading.

Source

Thrown at Lib/_pyio.py:1372

    A buffered reader object and buffered writer object put together to
    form a sequential IO object that can read and write. This is typically
    used with a socket or two-way pipe.

    reader and writer are RawIOBase objects that are readable and
    writeable respectively. If the buffer_size is omitted it defaults to
    DEFAULT_BUFFER_SIZE.
    """

    # XXX The usefulness of this (compared to having two separate IO
    # objects) is questionable.

    def __init__(self, reader, writer, buffer_size=DEFAULT_BUFFER_SIZE):
        """Constructor.

        The arguments are two RawIO instances.
        """
        if not reader.readable():
            raise OSError('"reader" argument must be readable.')

        if not writer.writable():
            raise OSError('"writer" argument must be writable.')

        self.reader = BufferedReader(reader, buffer_size)
        self.writer = BufferedWriter(writer, buffer_size)

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

    def readinto(self, b):
        return self.reader.readinto(b)

    def write(self, b):
        return self.writer.write(b)

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Swap the arguments if the reader and writer were reversed (the readable object goes first)
  2. Open the reader side with a read-capable mode, e.g. open(path, 'rb') instead of 'wb'
  3. For custom raw IO classes, override readable() to return True when the class genuinely supports read()

Example fix

# before
pair = io.BufferedRWPair(open('out.bin','wb'), open('in.bin','rb'))  # reader is write-only -> OSError

# after
pair = io.BufferedRWPair(open('in.bin','rb'), open('out.bin','wb'))
Defensive patterns

Strategy: type-guard

Validate before calling

def make_rwpair(reader, writer, buffer_size=8192):
    import io
    if not reader.readable():
        raise OSError('reader argument must be readable')
    if not writer.writable():
        raise OSError('writer argument must be writable')
    return io.BufferedRWPair(reader, writer, buffer_size)

Type guard

def is_readable_raw(obj):
    return hasattr(obj, 'readable') and callable(obj.readable) and obj.readable()

Try / catch

try:
    pair = io.BufferedRWPair(r, w)
except OSError as e:
    if 'readable' in str(e):
        r, w = w, r  # swapped args; retry once after sanity check
        pair = io.BufferedRWPair(r, w)
    else:
        raise

Prevention

When it happens

Trigger: io.BufferedRWPair(reader, writer) where reader.readable() returns False, e.g. passing a write-only FileIO opened with mode 'w', a socket wrapper whose readable() is False, or a custom RawIOBase subclass that forgot to override readable() to return True.

Common situations: Wrapping subprocess stdin/stdout or socket makefile objects in a BufferedRWPair and swapping the two arguments; passing a custom raw IO class whose readable()/writable() flags were never set; unit-test doubles that return False from readable().

Related errors


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