python/cpython · error · RuntimeError
unable to writelines; sendfile is in progress
Error message
unable to writelines; sendfile is in progress
What it means
Raised by _SelectorSocketTransport.writelines() as RuntimeError when the internal _empty_waiter future is set, i.e. a sendfile operation is currently in progress on the transport. writelines() would append to the write buffer that sendfile is waiting to drain, so asyncio rejects the call to keep the buffer state consistent.
Source
Thrown at Lib/asyncio/selector_events.py:1190
if self._empty_waiter is not None:
self._empty_waiter.set_result(None)
if self._closing:
self._call_connection_lost(None)
elif self._eof:
self._sock.shutdown(socket.SHUT_WR)
def write_eof(self):
if self._closing or self._eof:
return
self._eof = True
if not self._buffer:
self._sock.shutdown(socket.SHUT_WR)
def writelines(self, list_of_data):
if self._eof:
raise RuntimeError('Cannot call writelines() after write_eof()')
if self._empty_waiter is not None:
raise RuntimeError('unable to writelines; sendfile is in progress')
if not list_of_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
for data in list_of_data:
self._buffer.append(memoryview(data))
self._buffer_size += len(data)
self._write_ready()
# If the entire buffer couldn't be written, register a write handler
if self._buffer:
self._add_writer(self._sock_fd, self._write_ready)
self._maybe_pause_protocol()
View on GitHub (pinned to bc6749cc3b)
Solutions
- Await the sendfile() completion before calling writelines() on that transport.
- Wrap transport access in an asyncio.Lock so sendfile and buffered writes cannot interleave.
- Stop background writers for the connection before initiating sendfile.
Example fix
// before asyncio.create_task(loop.sendfile(transport, f)) transport.writelines(chunks) # RuntimeError // after await loop.sendfile(transport, f) transport.writelines(chunks)
Defensive patterns
Strategy: validation
Validate before calling
async def batched_send(transport, chunks, path=None):
if path:
with open(path, 'rb') as f:
await loop.sendfile(transport, f) # fully awaited first
transport.writelines(chunks) # safe: no sendfile pending Try / catch
try:
transport.writelines(chunks)
except RuntimeError as e:
if 'sendfile is in progress' in str(e):
await asyncio.shield(sendfile_task)
transport.writelines(chunks)
else:
raise Prevention
- One in-flight bulk operation (sendfile OR buffered writes) per transport at a time.
- Buffer chunks in a queue and flush with writelines only between sendfiles.
- Guard shared StreamWriters with a lock in multi-task handlers.
When it happens
Trigger: Calling transport.writelines(list_of_data) while loop.sendfile(transport, file) (or the transport sendfile path) has not yet completed on the same transport.
Common situations: Batch-flushing queued messages via writelines() from a background task while the request handler serves a file with sendfile on the same connection; log/heartbeat writers racing large file transfers.
Related errors
- unable to write; sendfile is in progress
- Empty waiter is already set
- d3-flame-graph library failed to load
- Cannot call writelines() after write_eof()
- offset must be a non-negative integer (got {!r})
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/0c70bfb1ba27c59b.
Report an issue: GitHub.