{"record":{"id":"b92392b18fecbbc3","repo":"python/cpython","slug":"e-strerror","errorCode":null,"errorMessage":"{e.strerror}","messagePattern":"\\{e\\.strerror\\}","errorType":"exception","errorClass":"BlockingIOError","httpStatus":null,"severity":"error","filePath":"Lib/_pyio.py","lineNumber":1293,"sourceCode":"            # partial writes\n            if len(self._write_buf) > self.buffer_size:\n                # We're full, so let's pre-flush the buffer.  (This may\n                # raise BlockingIOError with characters_written == 0.)\n                self._flush_unlocked()\n            before = len(self._write_buf)\n            self._write_buf.extend(b)\n            written = len(self._write_buf) - before\n            if len(self._write_buf) > self.buffer_size:\n                try:\n                    self._flush_unlocked()\n                except BlockingIOError as e:\n                    if len(self._write_buf) > self.buffer_size:\n                        # We've hit the buffer_size. We have to accept a partial\n                        # write and cut back our buffer.\n                        overage = len(self._write_buf) - self.buffer_size\n                        written -= overage\n                        self._write_buf = self._write_buf[:self.buffer_size]\n                        raise BlockingIOError(e.errno, e.strerror, written)\n            return written\n\n    def truncate(self, pos=None):\n        with self._write_lock:\n            self._flush_unlocked()\n            if pos is None:\n                pos = self.raw.tell()\n            return self.raw.truncate(pos)\n\n    def flush(self):\n        with self._write_lock:\n            self._flush_unlocked()\n\n    def _flush_unlocked(self):\n        if self.closed:\n            raise ValueError(\"flush on closed file\")\n        while self._write_buf:\n            try:","sourceCodeStart":1275,"sourceCodeEnd":1311,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pyio.py#L1275-L1311","documentation":"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.","triggerScenarios":"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.","commonSituations":"Non-blocking socket writers when the socket send buffer is full; asyncio/raw-socket bridges using BufferedWriter; high-throughput pipes where the reader stalls.","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)."],"exampleFix":"# before\ndef send(writer, data):\n    return writer.write(data)  # raises BlockingIOError unhandled\n\n# after\ndef send(writer, data):\n    view = memoryview(data)\n    while view:\n        try:\n            n = writer.write(view)\n        except BlockingIOError as e:\n            n = e.characters_written\n        view = view[n:]  # retry remainder when writable again","handlingStrategy":"retry","validationCode":"import selectors\nsel = selectors.DefaultSelector()\nsel.register(sock, selectors.EVENT_WRITE)\n\ndef writable() -> bool:\n    return bool(sel.select(0))","typeGuard":null,"tryCatchPattern":"def write_all(writer, data) -> int:\n    view = memoryview(data)\n    while view:\n        try:\n            n = writer.write(view)\n        except BlockingIOError as e:\n            n = e.characters_written\n        if n:\n            view = view[n:]\n        else:\n            sel.select()  # wait for writability, then retry\n    return len(data)","preventionTips":["Always honor characters_written before retrying.","Bound in-flight buffered bytes to apply backpressure early.","Prefer blocking mode unless you implement select/retry loops."],"tags":["python","io","buffered-writer","blockingioerror","nonblocking","eagain","partial-write"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}