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

  1. Verify you are connecting to the TCP bridge port (from unity-mcp-status-*.json unity_port), not the HTTP server port.
  2. Ensure the handshake line (WELCOME UNITY-MCP...) is fully consumed by readline() before the first read_frame call.
  3. 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

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


AI-assisted analysis of CoplayDev/unity-mcp@c21bf496bc (2026-08-13). Data as JSON: /api/errors/53755d42b2541ec1. Report an issue: GitHub.