{"record":{"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/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/web_response.py#L441-L477","documentation":"Raised by Response.write() when _payload_writer is None, i.e. prepare() was never called for this response. The writer is the channel that actually pushes bytes onto the socket; without it write() has nowhere to send data. The error prevents silently dropping the body.","triggerScenarios":"Calling `await resp.write(data)` on a StreamResponse without first `await resp.prepare(request)`. Also triggered when prepare() raises or is interrupted and code continues to write() in a finally/except block.","commonSituations":"New aiohttp users forgetting the prepare() step; refactoring that moves prepare() into a conditional branch that is skipped on an error path; middleware that writes to a response it did not prepare.","solutions":["Always call `await resp.prepare(request)` before any `resp.write()`.","Use the higher-level `Response(body=...)` for fixed-size bodies — it handles prepare/write_eof automatically.","In error/cleanup paths, track whether prepare() succeeded before attempting writes.","If prepare() may have been skipped on an exception, return a fresh Response instead of writing to the unprepared one."],"exampleFix":"// before\nasync def handler(request):\n    resp = StreamResponse()\n    await resp.write(b'hi')  # RuntimeError: no prepare()\n    return resp\n\n// after\nasync def handler(request):\n    resp = StreamResponse()\n    await resp.prepare(request)\n    await resp.write(b'hi')\n    await resp.write_eof()\n    return resp","handlingStrategy":"validation","validationCode":"async def write_safe(resp, data):\n    if getattr(resp, '_payload_writer', None) is None:\n        await resp.prepare(resp._req)\n    await resp.write(data)","typeGuard":"def is_prepared(resp) -> bool:\n    return getattr(resp, '_payload_writer', None) is not None","tryCatchPattern":null,"preventionTips":["Always await resp.prepare(request) before any write().","Prefer Response(body=...) for fixed payloads — it handles prepare/write_eof.","In error paths, return a fresh Response rather than writing to an unprepared one.","Centralize streaming logic in a helper that prepares unconditionally."],"tags":["aiohttp","web-response","streaming","lifecycle","prepare"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}