{"record":{"id":"6ee42153d0a31445","repo":"CoplayDev/unity-mcp","slug":"unexpected-handshake-from-server-line-r-6ee421","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_mcp.py","lineNumber":82,"sourceCode":"    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    # Server sends a single line handshake: \"WELCOME UNITY-MCP 1 FRAMING=1\\n\"\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_ping_frame() -> bytes:\n    return b\"ping\"\n\n\ndef make_execute_menu_item(menu_path: str) -> bytes:\n    # Retained for manual debugging; not used in normal stress runs\n    payload = {\"type\": \"execute_menu_item\", \"params\": {\n        \"action\": \"execute\", \"menu_path\": menu_path}}\n    return json.dumps(payload).encode(\"utf-8\")\n\n\nasync def client_loop(idx: int, host: str, port: int, stop_time: float, stats: dict):\n    reconnect_delay = 0.2\n    while time.time() < stop_time:\n        writer = None\n        try:","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/CoplayDev/unity-mcp/blob/c21bf496bca87d54e75bad048563c3adb1782081/tools/stress_mcp.py#L64-L100","documentation":"Raised by do_handshake() in stress_mcp.py when the first line from the server does not contain 'WELCOME UNITY-MCP'. The bridge sends 'WELCOME UNITY-MCP 1 FRAMING=1\\n' on connect; a mismatch means the connection was closed before the greeting (empty line) or the remote service is not the Unity bridge.","triggerScenarios":"Connecting to the wrong port (HTTP server, a different tool, or nothing); the bridge accepted then dropped the connection before sending the greeting; the bridge is still starting up and the connection was reset; the greeting protocol string was changed in a newer bridge version.","commonSituations":"discover_port returned the default 6400 but the bridge is on a different port; the editor hasn't finished initializing the bridge; connecting to the MCP Python server port instead of the Unity-side bridge.","solutions":["Check ~/.unity-mcp/unity-mcp-status-*.json for the current unity_port and pass it explicitly.","Ensure Unity is fully loaded with the MCP for Unity package before starting the stress test.","If the greeting changed in a bridge update, update the b'WELCOME UNITY-MCP' sentinel in do_handshake."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import json, socket\nfrom pathlib import Path\n\ndef bridge_ready(host: str, port: int) -> bool:\n    if not (0 < port < 65536):\n        return False\n    try:\n        with socket.create_connection((host, port), timeout=2.0) as s:\n            greeting = s.recv(128)\n            return b\"WELCOME UNITY-MCP\" in greeting\n    except OSError:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    await asyncio.wait_for(do_handshake(reader), timeout=TIMEOUT)\nexcept ConnectionError:\n    stats[\"disconnects\"] += 1\n    await asyncio.sleep(reconnect_delay)\n    continue","preventionTips":["Verify the bridge port and greeting via the status JSON before launching clients.","Ensure Unity is fully initialized before starting the stress test.","Update the sentinel string if the bridge greeting protocol changes."],"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"}