{"record":{"id":"485b58954e65fe4a","repo":"aio-libs/aiohttp","slug":"response-has-not-been-started","errorCode":null,"errorMessage":"Response has not been started","messagePattern":"Response has not been started","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_ws.py","lineNumber":505,"sourceCode":"    async def send_json_bytes(\n        self,\n        data: Any,\n        compress: int | None = None,\n        *,\n        dumps: JSONBytesEncoder,\n    ) -> None:\n        \"\"\"Send JSON data using a bytes-returning encoder as a binary frame.\n\n        Use this when your JSON encoder (like orjson) returns bytes\n        instead of str, avoiding the encode/decode overhead.\n        \"\"\"\n        await self.send_bytes(dumps(data), compress=compress)\n\n    async def write_eof(self) -> None:  # type: ignore[override]\n        if self._eof_sent:\n            return\n        if self._payload_writer is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        await self.close()\n        self._eof_sent = True\n\n    async def close(\n        self, *, code: int = WSCloseCode.OK, message: bytes = b\"\", drain: bool = True\n    ) -> bool:\n        \"\"\"Close websocket connection.\"\"\"\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        if self._closed:\n            return False\n        self._set_closed()\n\n        try:\n            await self._writer.close(code, message)\n            writer = self._payload_writer","sourceCodeStart":487,"sourceCodeEnd":523,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/web_ws.py#L487-L523","documentation":"Raised by WebSocketResponse.write_eof() when self._payload_writer is None. _payload_writer is set by StreamResponse.prepare() (the parent class), i.e. the HTTP response body must have been started. write_eof is normally invoked by the framework during connection teardown; reaching it with no payload writer means teardown ran on a response that never started.","triggerScenarios":"The server / runner calls write_eof() on a WebSocketResponse whose prepare() raised or was never awaited; an explicit `await ws.write_eof()` in user code before prepare(); connection cleanup racing after a handshake failure.","commonSituations":"Catching and swallowing an exception from prepare() then letting the handler return the ws (framework then tries to finalize/write_eof); a custom runner or middleware that writes EOF unconditionally.","solutions":["Ensure `await ws.prepare(request)` completes before any code path can return the response.","Do not call ws.write_eof() yourself — the framework closes the WebSocket via ws.close(); call close() instead.","If you catch exceptions around prepare(), re-raise or return a plain HTTP response instead of the unprepared ws.","Inspect ws.prepared before any manual EOF/close path."],"exampleFix":"// before\nasync def handler(request):\n    ws = web.WebSocketResponse()\n    try:\n        await ws.prepare(request)\n    except Exception:\n        pass\n    return ws  # framework calls write_eof -> raises\n\n// after\nasync def handler(request):\n    ws = web.WebSocketResponse()\n    await ws.prepare(request)\n    ...\n    await ws.close()\n    return ws","handlingStrategy":"validation","validationCode":"if ws.prepared:\n    await ws.write_eof()\n# better: do not call write_eof yourself; rely on the framework / ws.close()","typeGuard":"def ws_has_payload(ws: web.WebSocketResponse) -> bool:\n    return getattr(ws, '_payload_writer', None) is not None","tryCatchPattern":"try:\n    await ws.write_eof()\nexcept RuntimeError as e:\n    if 'not been started' in str(e):\n        pass  # nothing to end\n    else:\n        raise","preventionTips":["Never call ws.write_eof() yourself — call ws.close() and let the framework finalize.","Do not swallow exceptions from prepare(); if it fails, return a plain Response.","Assert ws.prepared before any teardown that could call write_eof."],"tags":["websocket","lifecycle","eof","server"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}