{"record":{"id":"f2313c415d005250","repo":"python/cpython","slug":"cannot-call-write-after-write-eof","errorCode":null,"errorMessage":"Cannot call write() after write_eof()","messagePattern":"Cannot call write\\(\\) after write_eof\\(\\)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/selector_events.py","lineNumber":1066,"sourceCode":"        except BaseException as exc:\n            self._fatal_error(\n                exc, 'Fatal error: protocol.eof_received() call failed.')\n            return\n\n        if keep_open:\n            # We're keeping the connection open so the\n            # protocol can write more, but we still can't\n            # receive more, so remove the reader callback.\n            self._loop._remove_reader(self._sock_fd)\n        else:\n            self.close()\n\n    def write(self, data):\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError(f'data argument must be a bytes, bytearray, or memoryview '\n                            f'object, not {type(data).__name__!r}')\n        if self._eof:\n            raise RuntimeError('Cannot call write() after write_eof()')\n        if self._empty_waiter is not None:\n            raise RuntimeError('unable to write; sendfile is in progress')\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        if not self._buffer:\n            # Optimization: try to send now.\n            try:\n                n = self._sock.send(data)\n            except (BlockingIOError, InterruptedError):\n                pass\n            except (SystemExit, KeyboardInterrupt):","sourceCodeStart":1048,"sourceCodeEnd":1084,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/selector_events.py#L1048-L1084","documentation":"Raised by _SelectorSocketTransport.write() as RuntimeError when write() is called after write_eof() has already been invoked on the same transport. write_eof() half-closes the socket (shutdown(SHUT_WR)) marking that no more data will be sent; any subsequent write attempt violates that contract and is rejected instead of silently dropped.","triggerScenarios":"Calling transport.write_eof() (or StreamWriter.write_eof()) and then transport.write(data) on the same connection; common when EOF is sent inside a finally block that runs before queued writes, or when a handler signals end-of-request then appends trailer data.","commonSituations":"HTTP/line protocols where the handler writes EOF early to signal end of request then tries to write a response; cleanup code that calls write_eof() on error paths while a background writer task is still producing data; bidirectional protocols misordering shutdown.","solutions":["Reorder logic so write_eof() is the very last operation on the transport, after all writes and drain() calls complete.","Track EOF state in your protocol/handler and skip or queue-then-drop writes once EOF was sent.","Cancel producer tasks before calling write_eof() so no concurrent write can race the half-close.","Check transport.is_closing() and your own eof flag before writing from callbacks."],"exampleFix":"// before\nwriter.write_eof()\nwriter.write(b\"late data\\n\")  # RuntimeError\n\n// after\nwriter.write(b\"early data\\n\")\nawait writer.drain()\nwriter.write_eof()  # always last","handlingStrategy":"validation","validationCode":"eof_sent = False\n\ndef safe_write(transport, data):\n    if eof_sent or transport.is_closing():\n        return False  # drop or raise your own error\n    transport.write(data)\n    return True\n\ndef close_write_side(transport):\n    global eof_sent\n    eof_sent = True\n    transport.write_eof()","typeGuard":null,"tryCatchPattern":"try:\n    transport.write(data)\nexcept RuntimeError as e:\n    if 'write_eof' in str(e):\n        log.debug('dropping write after EOF: %r', data[:64])\n    else:\n        raise","preventionTips":["Treat write_eof() as the terminal write operation; nothing may follow it.","Await producer tasks before half-closing so writes cannot race EOF.","Track EOF in your protocol state and guard every write site."],"tags":["asyncio","networking","transport","eof","protocol-ordering"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}