aio-libs/aiohttp · error · WebSocketError
1002
1002
Error message
Continuation frame for non started message
What it means
Identical semantics to error [4]: WebSocketError (1002 PROTOCOL_ERROR) for a CONTINUATION frame with no preceding non-final TEXT/BINARY frame. The difference is purely the implementation: this one is raised by the PURE-PYTHON reader (reader_py.py) used when Cython extensions are disabled (AIOHTTP_NO_EXTENSIONS=1) or not compiled. Behavior, close code, and surfacing (msg.type == WSMsgType.ERROR) are the same as the Cython variant.
Source
Thrown at aiohttp/_websocket/reader_py.py:205
except Exception as exc:
self._exc = exc
set_exception(self.queue, exc)
return EMPTY_FRAME_ERROR
return EMPTY_FRAME
def _handle_frame(
self,
fin: bool,
opcode: int | cython_int, # Union intended: Cython pxd uses C int
payload: bytes | bytearray,
compressed: int | cython_int, # Union intended: Cython pxd uses C int
) -> None:
msg: WSMessage
if opcode in {OP_CODE_TEXT, OP_CODE_BINARY, OP_CODE_CONTINUATION}:
# Validate continuation frames before processing
if opcode == OP_CODE_CONTINUATION and self._opcode == OP_CODE_NOT_SET:
raise WebSocketError(
WSCloseCode.PROTOCOL_ERROR,
"Continuation frame for non started message",
)
# load text/binary
if not fin:
# got partial frame payload
if opcode != OP_CODE_CONTINUATION:
self._opcode = opcode
self._partial += payload
return
has_partial = bool(self._partial)
if opcode == OP_CODE_CONTINUATION:
opcode = self._opcode
self._opcode = OP_CODE_NOT_SET
# previous frame was non finished
# we should get continuation opcodeView on GitHub (pinned to c0ef574e29)
Solutions
- Handle msg.type == WSMsgType.ERROR / code 1002 in the receive loop and close.
- Ensure the peer starts each fragmented message with a TEXT/BINARY frame before CONTINUATION.
- If you did not intend pure-Python mode, build the extensions ('pip install -e .' / make cythonize) to move to the Cython reader; the bug itself is on the peer.
- Rule out a frame-corrupting intermediary.
Example fix
# before
PYTHONPATH=. AIOHTTP_NO_EXTENSIONS=1 python app.py # reader_py.py path
msg = await ws.receive()
# after
msg = await ws.receive()
if msg.type is aiohttp.WSMsgType.ERROR:
log.warning('protocol error (pure-py reader): %s', msg.data)
await ws.close() Defensive patterns
Strategy: type-guard
Type guard
from aiohttp import WSMsgType
def is_ws_error(msg) -> bool:
return msg.type is WSMsgType.ERROR Try / catch
msg = await ws.receive()
if msg.type is aiohttp.WSMsgType.ERROR:
exc = msg.data # WebSocketError with .code == 1002
log.warning('continuation-without-start (pure-py reader): %s', exc)
await ws.close() Prevention
- Same protocol guard as the Cython reader: handle WSMsgType.ERROR and inspect msg.data.code.
- If you did not intend pure-Python mode, build the C extensions so the Cython reader is used.
- Ensure the peer starts each fragmented message with TEXT/BINARY before CONTINUATION.
When it happens
Trigger: Same as [4]: a CONTINUATION (0x0) frame arrives while self._opcode == OP_CODE_NOT_SET. You only see reader_py.py in the traceback when extensions are off (AIOHTTP_NO_EXTENSIONS=1) or the C extension import failed.
Common situations: Running with AIOHTTP_NO_EXTENSIONS=1 for debugging; a broken/missing Cython build (pip install without compiling extensions); CI environments that force pure-Python mode. The underlying peer bug is the same as [4].
Related errors
- 1002
- Invalid challenge response
- Invalid window size
- Extension for deflate not supported{ext}
- Compress wbits must between 9 and 15, zlib does not support
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/77659189f302f82e.json.
Report an issue: GitHub.