{"id":"4a37cdc80d251447","repo":"aio-libs/aiohttp","slug":"cannot-call-write-before-prepare","errorCode":null,"errorMessage":"Cannot call write() before prepare()","messagePattern":"Cannot call write\\(\\) before prepare\\(\\)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_response.py","lineNumber":459,"sourceCode":"        version = request.version\n        status_line = f\"HTTP/{version[0]}.{version[1]} {self._status} {self._reason}\"\n        await writer.write_headers(status_line, self._headers)\n\n        # Send headers immediately if not opted into buffering\n        if self._send_headers_immediately:\n            writer.send_headers()\n\n    async def write(\n        self, data: Union[bytes, bytearray, \"memoryview[int]\", \"memoryview[bytes]\"]\n    ) -> None:\n        assert isinstance(\n            data, (bytes, bytearray, memoryview)\n        ), \"data argument must be byte-ish (%r)\" % type(data)\n\n        if self._eof_sent:\n            raise RuntimeError(\"Cannot call write() after write_eof()\")\n        if self._payload_writer is None:\n            raise RuntimeError(\"Cannot call write() before prepare()\")\n\n        await self._payload_writer.write(data)\n\n    async def drain(self) -> None:\n        assert not self._eof_sent, \"EOF has already been sent\"\n        assert self._payload_writer is not None, \"Response has not been started\"\n        warnings.warn(\n            \"drain method is deprecated, use await resp.write()\",\n            DeprecationWarning,\n            stacklevel=2,\n        )\n        await self._payload_writer.drain()\n\n    async def write_eof(self, data: bytes = b\"\") -> None:\n        assert isinstance(\n            data, (bytes, bytearray, memoryview)\n        ), \"data argument must be byte-ish (%r)\" % type(data)\n","sourceCodeStart":441,"sourceCodeEnd":477,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/web_response.py#L441-L477","documentation":"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.","triggerScenarios":"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().","commonSituations":"Writing a streaming/SSE/WebSocket-style handler; forgetting that prepare() must be awaited before the first write; refactoring a Response into a StreamResponse.","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."],"exampleFix":"# before\nresp = StreamResponse()\nawait resp.write(b'hello')  # raises RuntimeError\n\n# after\nresp = StreamResponse()\nawait resp.prepare(request)\nawait resp.write(b'hello')","handlingStrategy":"validation","validationCode":"async def stream_write(resp, request, data):\n    if not resp.prepared:\n        await resp.prepare(request)\n    await resp.write(data)","typeGuard":"def is_prepared(resp) -> bool:\n    return resp.prepared","tryCatchPattern":null,"preventionTips":["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."],"tags":["http","streaming","lifecycle","prepare","response"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}