aio-libs/aiohttp · error · RuntimeError
Response has not been started
Error message
Response has not been started
What it means
Raised by WebSocketResponse.write_eof() when self._payload_writer is None. _payload_writer is set by the parent StreamResponse.prepare(); for a WebSocketResponse the real setup happens in its overridden prepare(). Calling write_eof before the response has been started (prepared) means there is no writer to flush EOF to.
Source
Thrown at aiohttp/web_ws.py:505
async def send_json_bytes(
self,
data: Any,
compress: int | None = None,
*,
dumps: JSONBytesEncoder,
) -> None:
"""Send JSON data using a bytes-returning encoder as a binary frame.
Use this when your JSON encoder (like orjson) returns bytes
instead of str, avoiding the encode/decode overhead.
"""
await self.send_bytes(dumps(data), compress=compress)
async def write_eof(self) -> None: # type: ignore[override]
if self._eof_sent:
return
if self._payload_writer is None:
raise RuntimeError("Response has not been started")
await self.close()
self._eof_sent = True
async def close(
self, *, code: int = WSCloseCode.OK, message: bytes = b"", drain: bool = True
) -> bool:
"""Close websocket connection."""
if self._writer is None:
raise RuntimeError("Call .prepare() first")
if self._closed:
return False
self._set_closed()
try:
await self._writer.close(code, message)
writer = self._payload_writerView on GitHub (pinned to c0ef574e29)
Solutions
- Ensure `await ws.prepare(request)` completes successfully before any code path that may call write_eof().
- In generic middleware, check `ws.prepared` (or `ws._payload_writer is not None`) before calling write_eof().
- Let the normal close() path handle end-of-stream instead of calling write_eof() directly.
Example fix
# before ws = web.WebSocketResponse() await ws.write_eof() # after ws = web.WebSocketResponse() await ws.prepare(request) await ws.write_eof() # delegates to close()
Defensive patterns
Strategy: validation
Validate before calling
async def safe_write_eof(ws: web.WebSocketResponse) -> None:
if ws._payload_writer is None:
return # nothing started, nothing to flush
await ws.write_eof() Type guard
def response_started(ws) -> bool:
return getattr(ws, "_payload_writer", None) is not None Try / catch
try:
await ws.write_eof()
except RuntimeError as e:
if "Response has not been started" not in str(e):
raise Prevention
- In middleware, check `ws.prepared` before calling write_eof().
- Prefer close() over a direct write_eof() for WebSockets.
- Avoid early returns that skip prepare() on the WebSocketResponse.
When it happens
Trigger: Invoking `await ws.write_eof()` on a WebSocketResponse that never had `await ws.prepare(request)` called; or framework/middleware code that calls write_eof() on a response object whose prepare() was skipped or failed before assigning the writer.
Common situations: Middleware or cleanup hooks that unconditionally call write_eof() on any response; a handler that returns early before prepare(); an exception path that aborts after constructing the WebSocketResponse but before prepare() completes.
Related errors
- WebSocket connection is closed.
- Compress wbits must between 9 and 15, zlib does not support
- 1007
- Cannot write to closing transport
- Call runner.setup() before making a site
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/485b58954e65fe4a.json.
Report an issue: GitHub.