{"record":{"id":"e9a38ed709d56c26","repo":"python/cpython","slug":"writer-argument-must-be-writable","errorCode":null,"errorMessage":"\"writer\" argument must be writable.","messagePattern":"\"writer\" argument must be writable\\.","errorType":"validation","errorClass":"OSError","httpStatus":null,"severity":"error","filePath":"Lib/_pyio.py","lineNumber":1375,"sourceCode":"\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\n    def peek(self, size=0):\n        return self.reader.peek(size)\n","sourceCodeStart":1357,"sourceCodeEnd":1393,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pyio.py#L1357-L1393","documentation":"Raised by io.BufferedRWPair.__init__ when the object passed as writer does not report itself as writable via its writable() method. It is the second of two constructor sanity checks (after the reader.readable() check) that the supplied raw IO objects really support the operations the pair will perform on them.","triggerScenarios":"io.BufferedRWPair(reader, writer) where writer.writable() returns False, e.g. a read-only FileIO opened with 'r', a socket wrapper that is read-only/shut down for writing, or a custom raw class whose writable() defaults to False.","commonSituations":"Passing two read streams (e.g. both ends opened read-only) to simulate a bidirectional channel; custom RawIOBase subclasses where only readable() was overridden; using a closed or half-shutdown socket object as the writer side.","solutions":["Pass a genuinely writable object as the second argument (e.g. open(path,'wb') or a write-capable socket wrapper)","Open the file with a write mode ('wb', 'ab', 'w+b') instead of 'rb'","For custom raw IO classes, override writable() to return True when write() is implemented"],"exampleFix":"# before\npair = io.BufferedRWPair(sock_r, open('log.bin','rb'))  # writer not writable -> OSError\n\n# after\npair = io.BufferedRWPair(sock_r, open('log.bin','wb'))","handlingStrategy":"type-guard","validationCode":"def make_rwpair(reader, writer, buffer_size=8192):\n    import io\n    if not writer.writable():\n        raise OSError('writer argument must be writable')\n    return io.BufferedRWPair(reader, writer, buffer_size)","typeGuard":"def is_writable_raw(obj):\n    return hasattr(obj, 'writable') and callable(obj.writable) and obj.writable()","tryCatchPattern":"try:\n    pair = io.BufferedRWPair(r, w)\nexcept OSError as e:\n    if 'writable' in str(e):\n        w = open(path, 'wb')\n        pair = io.BufferedRWPair(r, w)\n    else:\n        raise","preventionTips":["Open the writer side with a write mode ('wb','ab') by construction","For custom raw classes, override writable() alongside write()","Document which end is which when wrapping duplex transports like sockets"],"tags":["python","io","buffered-io","constructor","oserror"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}