aio-libs/aiohttp · error · RuntimeError
Cannot call write() before prepare()
Error message
Cannot call write() before prepare()
What it means
write() requires an active payload writer, which only exists after prepare()/_start() has wired up self._payload_writer. Calling write() before prepare() raises RuntimeError at line 458-459. The framework normally calls prepare() for you when you return a Response, but for manual StreamResponse usage you must call await resp.prepare(request) first.
Source
Thrown at aiohttp/web_response.py:459
version = request.version
status_line = f"HTTP/{version[0]}.{version[1]} {self._status} {self._reason}"
await writer.write_headers(status_line, self._headers)
# Send headers immediately if not opted into buffering
if self._send_headers_immediately:
writer.send_headers()
async def write(
self, data: Union[bytes, bytearray, "memoryview[int]", "memoryview[bytes]"]
) -> None:
assert isinstance(
data, (bytes, bytearray, memoryview)
), "data argument must be byte-ish (%r)" % type(data)
if self._eof_sent:
raise RuntimeError("Cannot call write() after write_eof()")
if self._payload_writer is None:
raise RuntimeError("Cannot call write() before prepare()")
await self._payload_writer.write(data)
async def drain(self) -> None:
assert not self._eof_sent, "EOF has already been sent"
assert self._payload_writer is not None, "Response has not been started"
warnings.warn(
"drain method is deprecated, use await resp.write()",
DeprecationWarning,
stacklevel=2,
)
await self._payload_writer.drain()
async def write_eof(self, data: bytes = b"") -> None:
assert isinstance(
data, (bytes, bytearray, memoryview)
), "data argument must be byte-ish (%r)" % type(data)
View on GitHub (pinned to c0ef574e29)
Solutions
- Always call `await resp.prepare(request)` before any `await resp.write(...)`.
- Use Response(text=...) / json_response() for non-streaming bodies — the framework handles prepare for you.
- For SSE, ensure prepare() runs before the first chunk.
Example fix
# before resp = StreamResponse() await resp.write(b'hello') # raises RuntimeError # after resp = StreamResponse() await resp.prepare(request) await resp.write(b'hello')
Defensive patterns
Strategy: validation
Validate before calling
async def stream_write(resp, request, data):
if not resp.prepared:
await resp.prepare(request)
await resp.write(data) Type guard
def is_prepared(resp) -> bool:
return resp.prepared Prevention
- Always `await resp.prepare(request)` before the first write for StreamResponse.
- For non-streaming bodies use Response/json_response to avoid manual prepare().
- Write a helper that prepares-then-writes to standardize the order.
When it happens
Trigger: Manually constructing a StreamResponse and calling await resp.write(data) without first awaiting resp.prepare(request). Returning a StreamResponse and writing in a background task before the handler awaited prepare().
Common situations: Writing a streaming/SSE/WebSocket-style handler; forgetting that prepare() must be awaited before the first write; refactoring a Response into a StreamResponse.
Related errors
- Cannot call write() after write_eof()
- Got more than {limit} bytes when reading: {line!r}.
- Can not decode content-encoding: brotli (br). Please install
- Can not decode content-encoding: zstandard (zstd). Please in
- Can not decode content-encoding: %s
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/4a37cdc80d251447.json.
Report an issue: GitHub.