python/cpython · error · OSError
write() returned incorrect number of bytes
Error message
write() returned incorrect number of bytes
What it means
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').
Source
Thrown at Lib/_pyio.py:1321
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:
if self.raw is None or self.closed:
return
# We have to release the lock and call self.flush() (which will
# probably just re-take the lock) in case flush has been overridden inView on GitHub (pinned to bc6749cc3b)
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.
Example fix
# before
class MyRaw(io.RawIOBase):
def write(self, b):
self.queue.append(b)
return len(self.queue) # wrong: total queued, can exceed len(b)
# after
class MyRaw(io.RawIOBase):
def write(self, b):
self.queue.append(b)
return len(b) # exactly what was accepted Defensive patterns
Strategy: type-guard
Validate before calling
n = raw.write(chunk)
assert 0 <= n <= len(chunk), f"raw.write returned {n} for {len(chunk)} bytes"
del chunk[:n] Type guard
def sane_write_count(raw, probe: bytes) -> bool:
n = raw.write(probe)
return n is None or 0 <= n <= len(probe) Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- self.raw should implement RawIOBase: it should not raise Blo
- "raw" argument must be writable.
- seek() returned an invalid position
- tell() returned an invalid position
- "raw" argument must be readable.
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/f93495ef461f1dd6.
Report an issue: GitHub.