python/cpython · error · RuntimeError
self.raw should implement RawIOBase: it should not raise Blo
Error message
self.raw should implement RawIOBase: it should not raise BlockingIOError
What it means
Raised by BufferedWriter._flush_unlocked when the underlying raw stream's write() raises BlockingIOError. The buffered layer's contract (RawIOBase) is that raw.write either writes or returns None to indicate blocking — it must not raise BlockingIOError itself. A raw object violating that contract is a programming error in the raw class, so it is surfaced as RuntimeError with the quoted expectation.
Source
Thrown at Lib/_pyio.py:1314
def truncate(self, pos=None):
with self._write_lock:
self._flush_unlocked()
if pos is None:
pos = self.raw.tell()
return self.raw.truncate(pos)
def flush(self):
with self._write_lock:
self._flush_unlocked()
def _flush_unlocked(self):
if self.closed:
raise ValueError("flush on closed file")
while self._write_buf:
try:
n = self.raw.write(self._write_buf)
except BlockingIOError:
raise RuntimeError("self.raw should implement RawIOBase: it "
"should not raise BlockingIOError")
if n is None:
raise BlockingIOError(
errno.EAGAIN,
"write could not complete without blocking", 0)
if n > len(self._write_buf) or n < 0:
raise OSError("write() returned incorrect number of bytes")
del self._write_buf[:n]
def tell(self):
return _BufferedIOMixin.tell(self) + len(self._write_buf)
def seek(self, pos, whence=0):
if whence not in valid_seek_flags:
raise ValueError("invalid whence value")
with self._write_lock:
self._flush_unlocked()
return _BufferedIOMixin.seek(self, pos, whence)View on GitHub (pinned to bc6749cc3b)
Solutions
- In the custom raw class, catch the blocking condition in write() and return None instead of raising BlockingIOError.
- Subclass io.RawIOBase (not IOBase) so the return-value contract is explicit.
- If blocking writes are fine, make the fd blocking so os.write never signals EAGAIN.
Example fix
# before
class MyRaw(io.RawIOBase):
def write(self, b):
return os.write(self.fd, b) # may raise BlockingIOError -> RuntimeError upstream
# after
class MyRaw(io.RawIOBase):
def write(self, b):
try:
return os.write(self.fd, b)
except BlockingIOError:
return None # RawIOBase protocol: None means 'would block' Defensive patterns
Strategy: type-guard
Validate before calling
class ContractCheckingRaw(io.RawIOBase):
def write(self, b):
try:
return super().write(b)
except BlockingIOError:
return None # satisfy RawIOBase protocol Type guard
def honors_rawio_contract(raw) -> bool:
"""Can't fully verify statically; check the class contract by inspection."""
import inspect
src = inspect.getsource(type(raw).write)
return "BlockingIOError" not in src or "return None" in src Try / catch
try:
writer.flush()
except RuntimeError as e:
if "RawIOBase" in str(e):
raise TypeError("custom raw stream violates RawIOBase: return None on would-block") from e
raise Prevention
- Subclass io.RawIOBase for raw streams used with buffered wrappers.
- On would-block, return None from write(), never raise BlockingIOError.
- Unit-test custom raw streams against io.BufferedWriter to catch contract breaks.
When it happens
Trigger: Supplying a custom raw stream whose write() raises BlockingIOError('EAGAIN') instead of returning None; wrapping a non-blocking object without honoring the RawIOBase return-None protocol.
Common situations: Hand-rolled raw wrappers around sockets/subprocess pipes that propagate BlockingIOError from os.write/send; porting code from a layer (e.g. socketserver) that expects raising semantics into io wrappers.
Related errors
- write() returned incorrect number of bytes
- "raw" argument must be writable.
- {e.strerror}
- EAGAIN
- readinto returned {n} outside buffer size {len(b)}
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/e83fe9fe6a63a50c.
Report an issue: GitHub.