{"record":{"id":"53b1fcf5e7bc3b66","repo":"RustPython/RustPython","slug":"unable-to-write-sendfile-is-in-progress","errorCode":null,"errorMessage":"unable to write; sendfile is in progress","messagePattern":"unable to write; sendfile is in progress","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/proactor_events.py","lineNumber":346,"sourceCode":"class _ProactorBaseWritePipeTransport(_ProactorBasePipeTransport,\n                                      transports.WriteTransport):\n    \"\"\"Transport for write pipes.\"\"\"\n\n    _start_tls_compatible = True\n\n    def __init__(self, *args, **kw):\n        super().__init__(*args, **kw)\n        self._empty_waiter = None\n\n    def write(self, data):\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError(\n                f\"data argument must be a bytes-like object, \"\n                f\"not {type(data).__name__}\")\n        if self._eof_written:\n            raise RuntimeError('write_eof() already called')\n        if self._empty_waiter is not None:\n            raise RuntimeError('unable to write; sendfile is in progress')\n\n        if not 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        # Observable states:\n        # 1. IDLE: _write_fut and _buffer both None\n        # 2. WRITING: _write_fut set; _buffer None\n        # 3. BACKED UP: _write_fut set; _buffer a bytearray\n        # We always copy the data, so the caller can't modify it\n        # while we're still waiting for the I/O to happen.\n        if self._write_fut is None:  # IDLE -> WRITING\n            assert self._buffer is None","sourceCodeStart":328,"sourceCodeEnd":364,"githubUrl":"https://github.com/RustPython/RustPython/blob/aaeab4f754b4f40efc0c8ab39cf7c4a3c35a8cfd/Lib/asyncio/proactor_events.py#L328-L364","documentation":"While a sendfile transfer is in progress, the proactor write transport installs an internal _empty_waiter future and rejects all other writes with RuntimeError('unable to write; sendfile is in progress'). Interleaving buffered writes with the zero-copy file transfer would corrupt the outgoing byte stream order, so the transport enforces exclusivity until sendfile completes and the waiter resets.","triggerScenarios":"A heartbeat/keep-alive task calling transport.write() on a connection while await loop.sock_sendfile(sock, file) is still running on it; issuing write() from a callback that fires during the sendfile window; firing sendfile as a background task and immediately writing the next request.","commonSituations":"HTTP servers streaming large static files with sendfile while periodic ping/metrics writers share the same connection; progress-report tasks racing a download; pipelined request handlers that assume writes queue transparently.","solutions":["Serialize operations: await the sendfile to completion before issuing any further write() on that transport","Gate periodic writers (heartbeats, metrics) with an asyncio.Event so they pause for the duration of the sendfile","If you truly need interleaved output, abandon sendfile and stream the file in chunks through regular write()"],"exampleFix":"# before\nasyncio.create_task(loop.sock_sendfile(sock, big_file))  # fire-and-forget\ntransport.write(b\"next request\")                          # RuntimeError\n\n# after\nawait loop.sock_sendfile(sock, big_file)                  # transfer completes first\ntransport.write(b\"next request\")                          # safe now","handlingStrategy":"validation","validationCode":"class ConnectionGate:\n    def __init__(self):\n        self.sending = asyncio.Event()  # set while sendfile is active\n    async def sendfile(self, loop, sock, file):\n        self.sending.set()\n        try:\n            await loop.sock_sendfile(sock, file)\n        finally:\n            self.sending.clear()\n    def can_write(self) -> bool:\n        return not self.sending.is_set()","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Enforce one in-flight send operation per connection; await sendfile rather than firing it in the background","Pause periodic writers (heartbeats, metrics) for the duration of a sendfile","If output must interleave with the transfer, use chunked write() instead of sendfile"],"tags":["asyncio","proactor","sendfile","concurrency","transport","windows","python"],"backgroundTag":"resource-busy","analyzedSha":"aaeab4f754b4f40efc0c8ab39cf7c4a3c35a8cfd","analyzedAt":"2026-08-17T00:37:52.100Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}