python/cpython · error · RuntimeError
Cannot call writelines() after write_eof()
Error message
Cannot call writelines() after write_eof()
What it means
Raised by _SelectorSocketTransport.writelines() as RuntimeError when called after write_eof() has marked the transport as half-closed. writelines() is the batched variant of write() and obeys the same lifecycle rule: once the write side is shut down no further outgoing data is accepted.
Source
Thrown at Lib/asyncio/selector_events.py:1188
if not self._buffer:
self._loop._remove_writer(self._sock_fd)
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)View on GitHub (pinned to bc6749cc3b)
Solutions
- Move writelines() before write_eof() so EOF is strictly the final transport operation.
- Guard writes with an `if transport.is_closing() or eof_sent: return` check in your protocol wrapper.
- Ensure producer tasks are done (await them) before half-closing the connection.
Example fix
// before transport.write_eof() transport.writelines([b"a\n", b"b\n"]) # RuntimeError // after transport.writelines([b"a\n", b"b\n"]) transport.write_eof()
Defensive patterns
Strategy: validation
Validate before calling
class WriteOnceEofTransport:
def __init__(self, transport):
self._t = transport
self._eof = False
def writelines(self, chunks):
if self._eof or self._t.is_closing():
return
self._t.writelines(chunks)
def write_eof(self):
self._eof = True
self._t.write_eof() Try / catch
try:
transport.writelines(chunks)
except RuntimeError as e:
if 'after write_eof' in str(e):
log.warning('dropped %d chunks after EOF', len(chunks))
else:
raise Prevention
- Order handlers so all body/batch writes precede the write_eof() call.
- Await drain() after the last writelines before half-closing.
- Unit-test protocol handlers for write-after-EOF orderings.
When it happens
Trigger: Calling transport.writelines([chunk1, chunk2]) or StreamWriter.writelines() after transport.write_eof() was already invoked on the same connection.
Common situations: Response-building code that calls write_eof() after finishing headers, then appends body chunks via writelines; shared handlers where one path half-closes early; refactoring write() loops into writelines() without revisiting the EOF ordering.
Related errors
- Cannot call write() after write_eof()
- data argument must be a bytes, bytearray, or memoryview obje
- unable to write; sendfile is in progress
- unable to writelines; sendfile is in progress
- Failed to fetch glossary.json
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/d644b2edf235b1c9.
Report an issue: GitHub.