RustPython/RustPython · error · OSError
"writer" argument must be writable.
Error message
"writer" argument must be writable.
What it means
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.
Source
Thrown at Lib/_pyio.py:1349
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)
def peek(self, size=0):
return self.reader.peek(size)
View on GitHub (pinned to aaeab4f754)
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
Example fix
# before
r, w = os.pipe()
pair = io.BufferedRWPair(io.FileIO(r, 'r'), io.FileIO(r, 'r')) # OSError: "writer" argument must be writable.
# after
pair = io.BufferedRWPair(io.FileIO(r, 'r'), io.FileIO(w, 'w'))
# custom writer side
class MyWriter(io.RawIOBase):
def writable(self):
return True
def write(self, b):
return os.write(self.fd, b) Defensive patterns
Strategy: validation
Validate before calling
if not writer.writable():
raise ValueError(f'{writer!r} cannot serve as the writer side')
pair = io.BufferedRWPair(reader, writer) Type guard
import io
def rw_pair_ready(reader, writer) -> bool:
"""True when both endpoints can fill their RWPair roles."""
return reader.readable() and writer.writable() Try / catch
try:
pair = io.BufferedRWPair(reader, writer)
except OSError as exc:
if 'must be writable' in str(exc):
raise ValueError(f'writer side is not writable: {writer!r}') from exc
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- "reader" argument must be readable.
- "raw" argument must be readable.
- "raw" argument must be writable.
- seek() returned an invalid position
- tell() returned an invalid position
AI-assisted analysis of RustPython/RustPython@aaeab4f754 (2026-08-17).
Data as JSON: /api/errors/04e88e7e24fcc6e9.
Report an issue: GitHub.