{"record":{"id":"fd3531a11b5fa195","repo":"CoplayDev/unity-mcp","slug":"unexpected-handshake-from-server-line-r","errorCode":null,"errorMessage":"Unexpected handshake from server: {line!r}","messagePattern":"Unexpected handshake from server: (.+?)","errorType":"exception","errorClass":"ConnectionError","httpStatus":null,"severity":"error","filePath":"tools/stress_editor_state.py","lineNumber":80,"sourceCode":"async def read_frame(reader: asyncio.StreamReader) -> bytes:\n    header = await read_exact(reader, 8)\n    (length,) = struct.unpack(\">Q\", header)\n    if length <= 0 or length > (64 * 1024 * 1024):\n        raise ValueError(f\"Invalid frame length: {length}\")\n    return await read_exact(reader, length)\n\n\nasync def write_frame(writer: asyncio.StreamWriter, payload: bytes) -> None:\n    header = struct.pack(\">Q\", len(payload))\n    writer.write(header)\n    writer.write(payload)\n    await asyncio.wait_for(writer.drain(), timeout=TIMEOUT)\n\n\nasync def do_handshake(reader: asyncio.StreamReader) -> None:\n    line = await reader.readline()\n    if not line or b\"WELCOME UNITY-MCP\" not in line:\n        raise ConnectionError(f\"Unexpected handshake from server: {line!r}\")\n\n\ndef make_get_editor_state_frame() -> bytes:\n    payload = {\"type\": \"get_editor_state\", \"params\": {}}\n    return json.dumps(payload).encode(\"utf-8\")\n\n\nasync def stress_loop(host: str, port: int, duration: float, interval: float, verbose: bool):\n    stop_time = time.time() + duration\n    stats = {\"requests\": 0, \"errors\": 0, \"reconnects\": 0}\n    \n    print(f\"Starting editor state stress test...\")\n    print(f\"  Target: {host}:{port}\")\n    print(f\"  Duration: {duration}s\")\n    print(f\"  Interval: {interval}s ({1/interval:.1f} requests/sec)\")\n    print(f\"  Press Ctrl+C to stop early\")\n    print()\n    ","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/CoplayDev/unity-mcp/blob/c21bf496bca87d54e75bad048563c3adb1782081/tools/stress_editor_state.py#L62-L98","documentation":"Raised by do_handshake() in stress_editor_state.py when the first line read from the server does not contain the bytes 'WELCOME UNITY-MCP'. The Unity MCP bridge sends a greeting line on connect; its absence means either the connection was closed immediately (empty line) or the remote service is not the MCP bridge.","triggerScenarios":"Connecting to a port that is not the Unity bridge (e.g. the HTTP server, a different Unity tool, or nothing at all); the bridge accepted the connection but crashed before sending the greeting; the bridge protocol changed and the greeting string was altered.","commonSituations":"Wrong port discovered or hardcoded; the bridge is mid-startup and hasn't sent the greeting yet; connecting to the Python MCP server's port instead of the Unity-side TCP bridge.","solutions":["Confirm the port from ~/.unity-mcp/unity-mcp-status-*.json (unity_port field) and pass it via --port.","Verify the Unity Editor is open with the MCP for Unity package active and the bridge started.","If the greeting string changed, update the b'WELCOME UNITY-MCP' check in do_handshake."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import json\nfrom pathlib import Path\n\ndef discover_port() -> int | None:\n    status_dir = Path.home() / \".unity-mcp\"\n    for f in sorted(status_dir.glob(\"unity-mcp-status-*.json\"), key=lambda p: p.stat().st_mtime, reverse=True):\n        data = json.loads(f.read_text())\n        port = int(data.get(\"unity_port\", 0) or 0)\n        if 0 < port < 65536:\n            return port\n    return None\n\nport = discover_port()\nif port is None:\n    raise SystemExit(\"No active Unity bridge found. Open Unity with MCP for Unity.\")","typeGuard":null,"tryCatchPattern":"try:\n    await asyncio.wait_for(do_handshake(reader), timeout=TIMEOUT)\nexcept ConnectionError as e:\n    print(f\"Handshake failed: {e}. Is the bridge running on port {port}?\")\n    raise","preventionTips":["Confirm the bridge port from the status JSON before connecting.","Ensure Unity is fully loaded and the bridge started.","If the greeting string changes in a bridge update, update the sentinel bytes."],"tags":["stress-test","network","tcp","protocol","handshake","unity-bridge"],"backgroundTag":null,"analyzedSha":"c21bf496bca87d54e75bad048563c3adb1782081","analyzedAt":"2026-08-13T17:36:56.095Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}