python/cpython · error · RuntimeError
unable to write; sendfile is in progress
Error message
unable to write; sendfile is in progress
What it means
Raised by _SelectorSocketTransport.write() as RuntimeError when a sendfile operation is in progress on the transport. During sendfile the transport sets _empty_waiter (an internal future awaiting an empty write buffer); concurrent write() calls are forbidden because they would corrupt the buffer state the sendfile completion logic depends on.
Source
Thrown at Lib/asyncio/selector_events.py:1068
exc, 'Fatal error: protocol.eof_received() call failed.')
return
if keep_open:
# We're keeping the connection open so the
# protocol can write more, but we still can't
# receive more, so remove the reader callback.
self._loop._remove_reader(self._sock_fd)
else:
self.close()
def write(self, data):
if not isinstance(data, (bytes, bytearray, memoryview)):
raise TypeError(f'data argument must be a bytes, bytearray, or memoryview '
f'object, not {type(data).__name__!r}')
if self._eof:
raise RuntimeError('Cannot call write() after write_eof()')
if self._empty_waiter is not None:
raise RuntimeError('unable to write; sendfile is in progress')
if not data:
return
if self._conn_lost:
if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES:
logger.warning('socket.send() raised exception.')
self._conn_lost += 1
return
if not self._buffer:
# Optimization: try to send now.
try:
n = self._sock.send(data)
except (BlockingIOError, InterruptedError):
pass
except (SystemExit, KeyboardInterrupt):
raise
except BaseException as exc:View on GitHub (pinned to bc6749cc3b)
Solutions
- Await the sendfile() call to completion before issuing any write() on that transport.
- Serialize access to the transport with an asyncio.Lock around sendfile/write sequences.
- If interleaving is required, avoid sendfile and stream the file with read()+write() instead (setbufsize / fallback mode).
- Cancel or pause background writer tasks for the duration of the sendfile.
Example fix
// before sendfile_task = asyncio.create_task(loop.sendfile(transport, f)) transport.write(b"next request?\n") # RuntimeError // after await loop.sendfile(transport, f) transport.write(b"next request?\n")
Defensive patterns
Strategy: validation
Validate before calling
conn_lock = asyncio.Lock()
async def send_file_then_write(transport, path, extra):
async with conn_lock:
with open(path, 'rb') as f:
await loop.sendfile(transport, f)
transport.write(extra) # only after sendfile resolves Try / catch
try:
transport.write(data)
except RuntimeError as e:
if 'sendfile is in progress' in str(e):
await sendfile_task # wait, then retry once
transport.write(data)
else:
raise Prevention
- Never let heartbeat/keepalive tasks write while sendfile is active; pause them.
- Serialize transport access with a per-connection asyncio.Lock.
- If writes must interleave file data, stream with read()+write() instead of sendfile.
When it happens
Trigger: Calling transport.write(data) (directly or via StreamWriter) while a loop.sendfile(transport, file) / StreamWriter.sendfile() call on the same transport is still awaiting.
Common situations: A server handler serving a file with sendfile while a heartbeat/keepalive task writes to the same connection; concurrent tasks sharing one StreamWriter where one sends files; protocols that interleave control frames with large file transfers.
Related errors
- d3-flame-graph library failed to load
- unable to writelines; sendfile is in progress
- Empty waiter is already set
- Transport is closing
- sendfile is not supported for transport {transport!r}
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/cfdadfecb8ff7248.
Report an issue: GitHub.