CoplayDev/unity-mcp · error · ValueError
Invalid frame length: {length}
Error message
Invalid frame length: {length} What it means
Raised by read_frame() in stress_editor_state.py after unpacking the 8-byte big-endian frame header. The decoded length must be in (0, 64MB]; values outside that range indicate the byte stream is desynchronized — the 8 bytes read as a header are actually payload or garbage, not a valid length prefix. A length of 0 or a value >64MiB means framing alignment is broken.
Source
Thrown at tools/stress_editor_state.py:66
pass
return default_port
async def read_exact(reader: asyncio.StreamReader, n: int) -> bytes:
buf = b""
while len(buf) < n:
chunk = await reader.read(n - len(buf))
if not chunk:
raise ConnectionError("Connection closed while reading")
buf += chunk
return buf
async def read_frame(reader: asyncio.StreamReader) -> bytes:
header = await read_exact(reader, 8)
(length,) = struct.unpack(">Q", header)
if length <= 0 or length > (64 * 1024 * 1024):
raise ValueError(f"Invalid frame length: {length}")
return await read_exact(reader, length)
async def write_frame(writer: asyncio.StreamWriter, payload: bytes) -> None:
header = struct.pack(">Q", len(payload))
writer.write(header)
writer.write(payload)
await asyncio.wait_for(writer.drain(), timeout=TIMEOUT)
async def do_handshake(reader: asyncio.StreamReader) -> None:
line = await reader.readline()
if not line or b"WELCOME UNITY-MCP" not in line:
raise ConnectionError(f"Unexpected handshake from server: {line!r}")
def make_get_editor_state_frame() -> bytes:
payload = {"type": "get_editor_state", "params": {}}View on GitHub (pinned to c21bf496bc)
Solutions
- Verify you are connecting to the TCP bridge port (from unity-mcp-status-*.json unity_port), not the HTTP server port.
- Ensure the handshake line (WELCOME UNITY-MCP...) is fully consumed by readline() before the first read_frame call.
- If framing is persistently broken, the protocol version may differ — check the bridge's framing negotiation.
Defensive patterns
Strategy: try-catch
Try / catch
try:
response = await asyncio.wait_for(read_frame(reader), timeout=TIMEOUT)
except (ValueError, ConnectionError, asyncio.TimeoutError) as e:
# Framing is desynchronized — close and reconnect with a fresh reader.
writer.close()
await writer.wait_closed()
reader, writer = await asyncio.open_connection(host, port)
await do_handshake(reader) Prevention
- Always reconnect after a framing error — do not reuse the desynchronized StreamReader.
- Confirm you are connecting to the TCP bridge port, not the HTTP MCP port.
- Ensure the handshake line is fully consumed before the first framed read.
When it happens
Trigger: Stream desynchronization: the reader consumed the wrong number of bytes on a prior frame (e.g. a handshake line was partially consumed before framing began), the server sent an unsolicited message that shifted alignment, or the client connected to a service that does not use the '>Q' length-prefixed framing protocol.
Common situations: Connecting to the wrong port (e.g. the HTTP MCP endpoint instead of the raw TCP bridge); the bridge sent extra bytes during/after the handshake line that the reader didn't drain; a prior read_frame call returned early due to a timeout leaving partial bytes in the buffer.
Related errors
- Invalid frame length: {length}
- Unexpected handshake from server: {line!r}
- Unexpected handshake from server: {line!r}
- Connection closed while reading
- Connection closed while reading
AI-assisted analysis of CoplayDev/unity-mcp@c21bf496bc (2026-08-13).
Data as JSON: /api/errors/53755d42b2541ec1.
Report an issue: GitHub.