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
- Swap the arguments if the reader and writer were reversed (the readable object goes first)
- Open the reader side with a read-capable mode, e.g. open(path, 'rb') instead of 'wb'
- 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
- Assert reader.readable() and writer.writable() in debug builds before constructing the pair
- Keep naming explicit (sock_in/sock_out) to avoid argument swaps
- Unit-test custom raw classes' readable()/writable() flags
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
- "writer" argument must be writable.
- "raw" argument must be readable.
- "raw" argument must be writable.
- write() returned incorrect number of bytes
- seek() returned invalid position
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/b939e91e72a3acb1.
Report an issue: GitHub.