{"record":{"id":"0c70bfb1ba27c59b","repo":"python/cpython","slug":"unable-to-writelines-sendfile-is-in-progress","errorCode":null,"errorMessage":"unable to writelines; sendfile is in progress","messagePattern":"unable to writelines; sendfile is in progress","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/selector_events.py","lineNumber":1190,"sourceCode":"                if self._empty_waiter is not None:\n                    self._empty_waiter.set_result(None)\n                if self._closing:\n                    self._call_connection_lost(None)\n                elif self._eof:\n                    self._sock.shutdown(socket.SHUT_WR)\n\n    def write_eof(self):\n        if self._closing or self._eof:\n            return\n        self._eof = True\n        if not self._buffer:\n            self._sock.shutdown(socket.SHUT_WR)\n\n    def writelines(self, list_of_data):\n        if self._eof:\n            raise RuntimeError('Cannot call writelines() after write_eof()')\n        if self._empty_waiter is not None:\n            raise RuntimeError('unable to writelines; sendfile is in progress')\n        if not list_of_data:\n            return\n\n        if self._conn_lost:\n            if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES:\n                logger.warning('socket.send() raised exception.')\n            self._conn_lost += 1\n            return\n\n        for data in list_of_data:\n            self._buffer.append(memoryview(data))\n            self._buffer_size += len(data)\n        self._write_ready()\n        # If the entire buffer couldn't be written, register a write handler\n        if self._buffer:\n            self._add_writer(self._sock_fd, self._write_ready)\n            self._maybe_pause_protocol()\n","sourceCodeStart":1172,"sourceCodeEnd":1208,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/selector_events.py#L1172-L1208","documentation":"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.","triggerScenarios":"Calling transport.writelines(list_of_data) while loop.sendfile(transport, file) (or the transport sendfile path) has not yet completed on the same transport.","commonSituations":"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.","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."],"exampleFix":"// before\nasyncio.create_task(loop.sendfile(transport, f))\ntransport.writelines(chunks)  # RuntimeError\n\n// after\nawait loop.sendfile(transport, f)\ntransport.writelines(chunks)","handlingStrategy":"validation","validationCode":"async def batched_send(transport, chunks, path=None):\n    if path:\n        with open(path, 'rb') as f:\n            await loop.sendfile(transport, f)  # fully awaited first\n    transport.writelines(chunks)  # safe: no sendfile pending","typeGuard":null,"tryCatchPattern":"try:\n    transport.writelines(chunks)\nexcept RuntimeError as e:\n    if 'sendfile is in progress' in str(e):\n        await asyncio.shield(sendfile_task)\n        transport.writelines(chunks)\n    else:\n        raise","preventionTips":["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."],"tags":["asyncio","networking","sendfile","writelines","concurrency"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}