{"record":{"id":"f4b68043e0d4bc1a","repo":"aio-libs/aiohttp","slug":"wsclosecode-protocol-error-f4b680","errorCode":"WSCloseCode.PROTOCOL_ERROR","errorMessage":"Continuation frame for non started message","messagePattern":"Continuation frame for non started message","errorType":"exception","errorClass":"WebSocketError","httpStatus":null,"severity":"error","filePath":"aiohttp/_websocket/reader_c.py","lineNumber":205,"sourceCode":"        except Exception as exc:\n            self._exc = exc\n            set_exception(self.queue, exc)\n            return EMPTY_FRAME_ERROR\n\n        return EMPTY_FRAME\n\n    def _handle_frame(\n        self,\n        fin: bool,\n        opcode: int | cython_int,  # Union intended: Cython pxd uses C int\n        payload: bytes | bytearray,\n        compressed: int | cython_int,  # Union intended: Cython pxd uses C int\n    ) -> None:\n        msg: WSMessage\n        if opcode in {OP_CODE_TEXT, OP_CODE_BINARY, OP_CODE_CONTINUATION}:\n            # Validate continuation frames before processing\n            if opcode == OP_CODE_CONTINUATION and self._opcode == OP_CODE_NOT_SET:\n                raise WebSocketError(\n                    WSCloseCode.PROTOCOL_ERROR,\n                    \"Continuation frame for non started message\",\n                )\n\n            # load text/binary\n            if not fin:\n                # got partial frame payload\n                if opcode != OP_CODE_CONTINUATION:\n                    self._opcode = opcode\n                self._partial += payload\n                return\n\n            has_partial = bool(self._partial)\n            if opcode == OP_CODE_CONTINUATION:\n                opcode = self._opcode\n                self._opcode = OP_CODE_NOT_SET\n            # previous frame was non finished\n            # we should get continuation opcode","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d9aaf697c2cd4783ca5749a971965c689f3ec24f/aiohttp/_websocket/reader_c.py#L187-L223","documentation":"Identical logic to error #4 but raised from the compiled Cython reader at reader_c.py:205 (the .pyx-backed WebSocketReader used when C extensions are built). It fires when a CONTINUATION opcode arrives while self._opcode == OP_CODE_NOT_SET (no in-progress fragmented message). The message text, WSCloseCode.PROTOCOL_ERROR, and behaviour are byte-for-byte the same as the pure-Python reader; only the active code path differs based on whether the extension was compiled.","triggerScenarios":"Same as #4: the remote peer sends a continuation (0x0) frame without a preceding non-final text/binary frame. Whether you hit #4 or #19 depends only on whether aiohttp loaded the Cython reader (default when the extension is built) versus the pure-Python fallback (AIOHTTP_NO_EXTENSIONS=1).","commonSituations":"A non-conformant or buggy peer sending orphan continuation frames; reproduced consistently because the Cython reader is the default in pip-installed aiohttp; surfaces only as the pure-Python error #4 if extensions are disabled.","solutions":["Handle WSMsgType.ERROR in the receive loop and close cleanly with `await ws.close()`.","If you control the peer, only send continuation frames after a FIN=0 text/binary frame.","Reconnect after PROTOCOL_ERROR; the stream cannot be recovered."],"exampleFix":"// before\nasync for msg in ws:\n    handle(msg.data)\n\n// after\nasync for msg in ws:\n    if msg.type == aiohttp.WSMsgType.ERROR:\n        await ws.close()\n        break\n    handle(msg.data)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"msg = await ws.receive()\nif msg.type == aiohttp.WSMsgType.ERROR:\n    err = msg.data  # WebSocketError; .code == WSCloseCode.PROTOCOL_ERROR\n    await ws.close()\n    return","preventionTips":["Always branch on WSMsgType.ERROR; this Cython-path error has identical semantics to the pure-Python one.","Only emit continuation frames after a FIN=0 data frame if you own the peer.","Treat PROTOCOL_ERROR as fatal: close and reconnect."],"tags":["websocket","protocol","rfc6455","fragmentation","continuation","cython"],"analyzedSha":"d9aaf697c2cd4783ca5749a971965c689f3ec24f","analyzedAt":"2026-08-06T21:30:48.638Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}