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

  1. Catch BlockingIOError and retry the unwritten remainder, using e.characters_written to know what was accepted.
  2. Switch the underlying stream to blocking mode if partial writes are not handled.
  3. 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

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


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/b92392b18fecbbc3. Report an issue: GitHub.