{"record":{"id":"b939e91e72a3acb1","repo":"python/cpython","slug":"reader-argument-must-be-readable","errorCode":null,"errorMessage":"\"reader\" argument must be readable.","messagePattern":"\"reader\" argument must be readable\\.","errorType":"validation","errorClass":"OSError","httpStatus":null,"severity":"error","filePath":"Lib/_pyio.py","lineNumber":1372,"sourceCode":"    A buffered reader object and buffered writer object put together to\n    form a sequential IO object that can read and write. This is typically\n    used with a socket or two-way pipe.\n\n    reader and writer are RawIOBase objects that are readable and\n    writeable respectively. If the buffer_size is omitted it defaults to\n    DEFAULT_BUFFER_SIZE.\n    \"\"\"\n\n    # XXX The usefulness of this (compared to having two separate IO\n    # objects) is questionable.\n\n    def __init__(self, reader, writer, buffer_size=DEFAULT_BUFFER_SIZE):\n        \"\"\"Constructor.\n\n        The arguments are two RawIO instances.\n        \"\"\"\n        if not reader.readable():\n            raise OSError('\"reader\" argument must be readable.')\n\n        if not writer.writable():\n            raise OSError('\"writer\" argument must be writable.')\n\n        self.reader = BufferedReader(reader, buffer_size)\n        self.writer = BufferedWriter(writer, buffer_size)\n\n    def read(self, size=-1):\n        if size is None:\n            size = -1\n        return self.reader.read(size)\n\n    def readinto(self, b):\n        return self.reader.readinto(b)\n\n    def write(self, b):\n        return self.writer.write(b)\n","sourceCodeStart":1354,"sourceCodeEnd":1390,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pyio.py#L1354-L1390","documentation":"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.","triggerScenarios":"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.","commonSituations":"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().","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()"],"exampleFix":"# before\npair = io.BufferedRWPair(open('out.bin','wb'), open('in.bin','rb'))  # reader is write-only -> OSError\n\n# after\npair = io.BufferedRWPair(open('in.bin','rb'), open('out.bin','wb'))","handlingStrategy":"type-guard","validationCode":"def make_rwpair(reader, writer, buffer_size=8192):\n    import io\n    if not reader.readable():\n        raise OSError('reader argument must be readable')\n    if not writer.writable():\n        raise OSError('writer argument must be writable')\n    return io.BufferedRWPair(reader, writer, buffer_size)","typeGuard":"def is_readable_raw(obj):\n    return hasattr(obj, 'readable') and callable(obj.readable) and obj.readable()","tryCatchPattern":"try:\n    pair = io.BufferedRWPair(r, w)\nexcept OSError as e:\n    if 'readable' in str(e):\n        r, w = w, r  # swapped args; retry once after sanity check\n        pair = io.BufferedRWPair(r, w)\n    else:\n        raise","preventionTips":["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"],"tags":["python","io","buffered-io","constructor","oserror"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}