python/cpython · error · BlockingIOError
{e.strerror}
Error message
{e.strerror} What it means
This entry is the re-raised BlockingIOError from BufferedWriter.write: when flushing the oversized buffer raises BlockingIOError (non-blocking raw stream), the writer trims the buffer to buffer_size, adjusts the count, and re-raises BlockingIOError(e.errno, e.strerror, written) — propagating the OS strerror text and reporting how many bytes were accepted (the third arg, characters_written). It signals a partial write on a non-blocking stream.
Source
Thrown at Lib/_pyio.py:1293
# partial writes
if len(self._write_buf) > self.buffer_size:
# We're full, so let's pre-flush the buffer. (This may
# raise BlockingIOError with characters_written == 0.)
self._flush_unlocked()
before = len(self._write_buf)
self._write_buf.extend(b)
written = len(self._write_buf) - before
if len(self._write_buf) > self.buffer_size:
try:
self._flush_unlocked()
except BlockingIOError as e:
if len(self._write_buf) > self.buffer_size:
# We've hit the buffer_size. We have to accept a partial
# write and cut back our buffer.
overage = len(self._write_buf) - self.buffer_size
written -= overage
self._write_buf = self._write_buf[:self.buffer_size]
raise BlockingIOError(e.errno, e.strerror, written)
return written
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:View on GitHub (pinned to bc6749cc3b)
Solutions
- Catch BlockingIOError and retry the unwritten remainder, using e.characters_written to know what was accepted.
- Switch the underlying stream to blocking mode if partial writes are not handled.
- Reduce write sizes below buffer_size so buffering absorbs bursts, or implement backpressure (pause writes until the fd is writable).
Example fix
# before
def send(writer, data):
return writer.write(data) # raises BlockingIOError unhandled
# after
def send(writer, data):
view = memoryview(data)
while view:
try:
n = writer.write(view)
except BlockingIOError as e:
n = e.characters_written
view = view[n:] # retry remainder when writable again Defensive patterns
Strategy: retry
Validate before calling
import selectors
sel = selectors.DefaultSelector()
sel.register(sock, selectors.EVENT_WRITE)
def writable() -> bool:
return bool(sel.select(0)) Try / catch
def write_all(writer, data) -> int:
view = memoryview(data)
while view:
try:
n = writer.write(view)
except BlockingIOError as e:
n = e.characters_written
if n:
view = view[n:]
else:
sel.select() # wait for writability, then retry
return len(data) Prevention
- Always honor characters_written before retrying.
- Bound in-flight buffered bytes to apply backpressure early.
- Prefer blocking mode unless you implement select/retry loops.
When it happens
Trigger: Writing more than buffer_size to a BufferedWriter over a non-blocking raw object (non-blocking socket via makefile, os.set_blocking(fd, False)) so raw.write raises EAGAIN; the message shown is e.strerror from the original OS error.
Common situations: Non-blocking socket writers when the socket send buffer is full; asyncio/raw-socket bridges using BufferedWriter; high-throughput pipes where the reader stalls.
Related errors
- EAGAIN
- "raw" argument must be writable.
- self.raw should implement RawIOBase: it should not raise Blo
- write() returned incorrect number of bytes
- peek on closed file
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/b92392b18fecbbc3.
Report an issue: GitHub.