{"record":{"id":"f93495ef461f1dd6","repo":"python/cpython","slug":"write-returned-incorrect-number-of-bytes","errorCode":null,"errorMessage":"write() returned incorrect number of bytes","messagePattern":"write\\(\\) returned incorrect number of bytes","errorType":"exception","errorClass":"OSError","httpStatus":null,"severity":"error","filePath":"Lib/_pyio.py","lineNumber":1321,"sourceCode":"    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:\n                n = self.raw.write(self._write_buf)\n            except BlockingIOError:\n                raise RuntimeError(\"self.raw should implement RawIOBase: it \"\n                                   \"should not raise BlockingIOError\")\n            if n is None:\n                raise BlockingIOError(\n                    errno.EAGAIN,\n                    \"write could not complete without blocking\", 0)\n            if n > len(self._write_buf) or n < 0:\n                raise OSError(\"write() returned incorrect number of bytes\")\n            del self._write_buf[:n]\n\n    def tell(self):\n        return _BufferedIOMixin.tell(self) + len(self._write_buf)\n\n    def seek(self, pos, whence=0):\n        if whence not in valid_seek_flags:\n            raise ValueError(\"invalid whence value\")\n        with self._write_lock:\n            self._flush_unlocked()\n            return _BufferedIOMixin.seek(self, pos, whence)\n\n    def close(self):\n        with self._write_lock:\n            if self.raw is None or self.closed:\n                return\n        # We have to release the lock and call self.flush() (which will\n        # probably just re-take the lock) in case flush has been overridden in","sourceCodeStart":1303,"sourceCodeEnd":1339,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pyio.py#L1303-L1339","documentation":"Raised by BufferedWriter._flush_unlocked when the raw stream's write() returns an out-of-range byte count — more than the supplied buffer length, or a negative number. Such a return value breaks the writer's bookkeeping (it does 'del self._write_buf[:n]'), so it is treated as a broken raw implementation and surfaced as OSError('write() returned incorrect number of bytes').","triggerScenarios":"Custom raw classes that return len(data) unconditionally (even on short writes), return the total cached length instead of the written count, or return -1 error sentinels; rarely, wrapper math bugs computing n from multiple writes.","commonSituations":"Buggy hand-written RawIOBase subclasses; adapters over C libraries whose write returns bytes-consumed semantics that include buffered-elsewhere bytes; mocks/fakes in tests returning wrong counts.","solutions":["Make the custom raw write() return exactly the number of bytes actually written, in [0, len(b)].","For would-block, return None (RawIOBase protocol) rather than -1.","Add a unit test asserting 0 <= raw.write(b) <= len(b) for all paths."],"exampleFix":"# before\nclass MyRaw(io.RawIOBase):\n    def write(self, b):\n        self.queue.append(b)\n        return len(self.queue)  # wrong: total queued, can exceed len(b)\n\n# after\nclass MyRaw(io.RawIOBase):\n    def write(self, b):\n        self.queue.append(b)\n        return len(b)  # exactly what was accepted","handlingStrategy":"type-guard","validationCode":"n = raw.write(chunk)\nassert 0 <= n <= len(chunk), f\"raw.write returned {n} for {len(chunk)} bytes\"\ndel chunk[:n]","typeGuard":"def sane_write_count(raw, probe: bytes) -> bool:\n    n = raw.write(probe)\n    return n is None or 0 <= n <= len(probe)","tryCatchPattern":null,"preventionTips":["Return exactly the bytes written from custom write().","Use None (not -1) for would-block in RawIOBase subclasses.","Test custom raw streams against BufferedWriter flush paths."],"tags":["python","io","buffered-writer","oserror","custom-stream","rawiobase","contract-violation"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}