python/cpython · warning · BlockingIOError

EAGAIN

EAGAIN

Error message

write could not complete without blocking

What it means

Raised by BufferedWriter._flush_unlocked when raw.write() returns None — the RawIOBase signal for 'would block' — meaning no bytes could be written right now. The buffer converts that into BlockingIOError(errno.EAGAIN, 'write could not complete without blocking', 0), i.e. a zero-byte partial write, so the caller knows to retry later.

Source

Thrown at Lib/_pyio.py:1317

            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)

    def close(self):
        with self._write_lock:

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Catch BlockingIOError from flush/write and retry once the fd reports writable (selectors.select for EVENT_WRITE).
  2. Put the fd back in blocking mode if you cannot implement backpressure.
  3. Bound the buffered amount yourself (stop writing when pending bytes exceed a watermark) so flush rarely hits EAGAIN.

Example fix

# before
writer.flush()  # BlockingIOError on full non-blocking socket

# after
import selectors
sel = selectors.DefaultSelector()
sel.register(sock, selectors.EVENT_WRITE)
while True:
    try:
        writer.flush()
        break
    except BlockingIOError:
        sel.select()  # wait until writable, then retry flush
Defensive patterns

Strategy: retry

Validate before calling

import selectors
sel = selectors.DefaultSelector()
sel.register(sock, selectors.EVENT_WRITE)
if not sel.select(0):
    ...  # not writable yet; defer flush
else:
    writer.flush()

Try / catch

while True:
    try:
        writer.flush()
        break
    except BlockingIOError:
        sel.select()  # wait for EVENT_WRITE, retry

Prevention

When it happens

Trigger: Flushing a BufferedWriter over a non-blocking socket/pipe whose send buffer is full (raw.write returns None); flushing after the peer stopped reading; BufferedWriter.seek()/truncate() on a full non-blocking stream (they flush internally).

Common situations: Non-blocking network writers under backpressure; pipe-based producers outrunning consumers; mixing blocking assumptions (assuming flush always completes) with non-blocking fds.

Related errors


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