{"record":{"id":"04e88e7e24fcc6e9","repo":"RustPython/RustPython","slug":"writer-argument-must-be-writable","errorCode":null,"errorMessage":"\"writer\" argument must be writable.","messagePattern":"\"writer\" argument must be writable\\.","errorType":"exception","errorClass":"OSError","httpStatus":null,"severity":"error","filePath":"Lib/_pyio.py","lineNumber":1349,"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":1331,"sourceCodeEnd":1367,"githubUrl":"https://github.com/RustPython/RustPython/blob/aaeab4f754b4f40efc0c8ab39cf7c4a3c35a8cfd/Lib/_pyio.py#L1331-L1367","documentation":"BufferedRWPair(reader, writer) checks the second endpoint after the first: if writer.writable() returns false it raises OSError('\"writer\" argument must be writable.'). The pair needs a genuinely writable raw stream to build its internal BufferedWriter, and the io.IOBase default for writable() is False, so custom classes must override it.","triggerScenarios":"io.BufferedRWPair(reader, read_end) with the pipe's read end passed as the writer; a custom writer class without a writable() override; passing a BufferedReader or a makefile('rb') object as the writer argument.","commonSituations":"Swapped ends when wiring subprocess pipes or socketpairs; custom transports that only implemented read-side methods; copy-paste constructor calls where both arguments were filled from the same source.","solutions":["Pass the write end as the second argument: BufferedRWPair(read_end, write_end)","Override writable() -> True and implement write() on the custom writer class","Validate both capabilities before constructing the pair","Use BufferedRandom for a single bidirectional seekable stream instead"],"exampleFix":"# before\nr, w = os.pipe()\npair = io.BufferedRWPair(io.FileIO(r, 'r'), io.FileIO(r, 'r'))  # OSError: \"writer\" argument must be writable.\n\n# after\npair = io.BufferedRWPair(io.FileIO(r, 'r'), io.FileIO(w, 'w'))\n\n# custom writer side\nclass MyWriter(io.RawIOBase):\n    def writable(self):\n        return True\n    def write(self, b):\n        return os.write(self.fd, b)","handlingStrategy":"validation","validationCode":"if not writer.writable():\n    raise ValueError(f'{writer!r} cannot serve as the writer side')\npair = io.BufferedRWPair(reader, writer)","typeGuard":"import io\n\ndef rw_pair_ready(reader, writer) -> bool:\n    \"\"\"True when both endpoints can fill their RWPair roles.\"\"\"\n    return reader.readable() and writer.writable()","tryCatchPattern":"try:\n    pair = io.BufferedRWPair(reader, writer)\nexcept OSError as exc:\n    if 'must be writable' in str(exc):\n        raise ValueError(f'writer side is not writable: {writer!r}') from exc\n    raise","preventionTips":["Double-check end assignment before constructing the pair","Custom writer classes must override writable() (IOBase default is False)","Validate both endpoints in the wiring helper","Write a smoke test that reads and writes through the pair"],"tags":["python","io","bufferedrwpair","raw-stream","constructor","oserror"],"backgroundTag":"io-stream-mode-mismatch","analyzedSha":"aaeab4f754b4f40efc0c8ab39cf7c4a3c35a8cfd","analyzedAt":"2026-08-17T00:37:52.100Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}