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

  1. Always call `await resp.prepare(request)` before any `await resp.write(...)`.
  2. Use Response(text=...) / json_response() for non-streaming bodies — the framework handles prepare for you.
  3. 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

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


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/4a37cdc80d251447.json. Report an issue: GitHub.